added prepare and tests
This commit is contained in:
parent
1db5c662d2
commit
b63e3d69ed
@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_CURVE_25519_BACKUP
|
||||
import org.matrix.android.sdk.api.listeners.ProgressListener
|
||||
import org.matrix.android.sdk.api.listeners.StepProgressListener
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeyBackupConfig
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupLastVersionResult
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupStateListener
|
||||
@ -46,9 +47,12 @@ import org.matrix.android.sdk.api.session.crypto.keysbackup.extractCurveKeyFromR
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.toKeysVersionResult
|
||||
import org.matrix.android.sdk.api.session.crypto.model.ImportRoomKeysResult
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||
import org.matrix.android.sdk.common.RetryTestRule
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
import org.matrix.android.sdk.common.TestConstants
|
||||
import org.matrix.android.sdk.common.waitFor
|
||||
import org.matrix.android.sdk.internal.crypto.keysbackup.algorithm.KeysBackupAlgorithmFactory
|
||||
@ -63,10 +67,21 @@ import kotlin.coroutines.resume
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
@LargeTest
|
||||
class KeysBackupTest : InstrumentedTest {
|
||||
open class KeysBackupTest : InstrumentedTest {
|
||||
|
||||
@get:Rule val rule = RetryTestRule(3)
|
||||
|
||||
@Test
|
||||
fun default_config_should_be_assymetric_only() = runSessionTest(context()) { testHelper ->
|
||||
val session = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||
|
||||
val defaultConfig = session.cryptoService().keysBackupService().keyBackupConfig
|
||||
|
||||
assertEquals(MXCRYPTO_ALGORITHM_CURVE_25519_BACKUP, defaultConfig.defaultAlgorithm)
|
||||
assertEquals(1, defaultConfig.supportedAlgorithms.size)
|
||||
assertTrue(defaultConfig.supportedAlgorithms.contains(MXCRYPTO_ALGORITHM_CURVE_25519_BACKUP))
|
||||
}
|
||||
|
||||
/**
|
||||
* - From doE2ETestWithAliceAndBobInARoomWithEncryptedMessages, we should have no backed up keys
|
||||
* - Check backup keys after having marked one as backed up
|
||||
@ -207,7 +222,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun backupAfterCreateKeysBackupVersionTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||
|
||||
@ -248,7 +263,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun backupAllGroupSessionsTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||
|
||||
@ -293,7 +308,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun testEncryptAndDecryptKeysBackupData() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||
|
||||
@ -342,7 +357,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun restoreKeysBackupTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
||||
|
||||
@ -428,7 +443,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun trustKeyBackupVersionTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
// - Do an e2e backup to the homeserver with a recovery key
|
||||
// - And log Alice on a new device
|
||||
@ -488,7 +503,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun trustKeyBackupVersionWithRecoveryKeyTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
// - Do an e2e backup to the homeserver with a recovery key
|
||||
// - And log Alice on a new device
|
||||
@ -546,7 +561,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun trustKeyBackupVersionWithWrongRecoveryKeyTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
// - Do an e2e backup to the homeserver with a recovery key
|
||||
// - And log Alice on a new device
|
||||
@ -588,7 +603,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun trustKeyBackupVersionWithPasswordTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
val password = "Password"
|
||||
|
||||
@ -648,7 +663,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun trustKeyBackupVersionWithWrongPasswordTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
val password = "Password"
|
||||
val badPassword = "Bad Password"
|
||||
@ -689,7 +704,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun restoreKeysBackupWithAWrongRecoveryKeyTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
||||
val keysBackupService = testData.aliceSession2.cryptoService().keysBackupService()
|
||||
@ -717,7 +732,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun testBackupWithPassword() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
val password = "password"
|
||||
|
||||
@ -773,7 +788,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun restoreKeysBackupWithAWrongPasswordTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
val password = "password"
|
||||
val wrongPassword = "passw0rd"
|
||||
@ -804,7 +819,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun testUseRecoveryKeyToRestoreAPasswordBasedKeysBackup() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
val password = "password"
|
||||
|
||||
@ -833,7 +848,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun testUsePasswordToRestoreARecoveryKeyBasedKeysBackup() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
||||
val keysBackupService = testData.aliceSession2.cryptoService().keysBackupService()
|
||||
@ -859,7 +874,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun testIsKeysBackupTrusted() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
// - Create a backup version
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||
@ -904,7 +919,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun testBackupWhenAnotherBackupWasCreated() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
// - Create a backup version
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||
@ -979,7 +994,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun testBackupAfterVerifyingADevice() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
// - Create a backup version
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||
@ -1066,7 +1081,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||
*/
|
||||
@Test
|
||||
fun deleteKeysBackupTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
// - Create a backup version
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||
@ -1089,4 +1104,9 @@ class KeysBackupTest : InstrumentedTest {
|
||||
|
||||
stateObserver.stopAndCheckStates(null)
|
||||
}
|
||||
|
||||
open var keyBackupConfig: KeyBackupConfig? = null
|
||||
|
||||
private fun createKeysBackupTestHelper(testHelper: CommonTestHelper, cryptoTestHelper: CryptoTestHelper) =
|
||||
KeysBackupTestHelper(testHelper, cryptoTestHelper, keyBackupConfig)
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import org.junit.Assert
|
||||
import org.matrix.android.sdk.api.listeners.ProgressListener
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeyBackupConfig
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupService
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupStateListener
|
||||
@ -34,7 +35,8 @@ import kotlin.coroutines.resume
|
||||
|
||||
internal class KeysBackupTestHelper(
|
||||
private val testHelper: CommonTestHelper,
|
||||
private val cryptoTestHelper: CryptoTestHelper
|
||||
private val cryptoTestHelper: CryptoTestHelper,
|
||||
private val keyBackupConfig: KeyBackupConfig? = null
|
||||
) {
|
||||
|
||||
fun waitForKeybackUpBatching() {
|
||||
@ -55,6 +57,9 @@ internal class KeysBackupTestHelper(
|
||||
|
||||
val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService).store
|
||||
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
|
||||
if (keyBackupConfig != null) {
|
||||
keysBackup.keyBackupConfig = keyBackupConfig
|
||||
}
|
||||
|
||||
val stateObserver = StateObserver(keysBackup)
|
||||
|
||||
@ -81,7 +86,10 @@ internal class KeysBackupTestHelper(
|
||||
|
||||
// - Log Alice on a new device
|
||||
val aliceSession2 = testHelper.logIntoAccount(aliceUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync)
|
||||
|
||||
if (keyBackupConfig != null) {
|
||||
aliceSession2.cryptoService().keysBackupService().keyBackupConfig = keyBackupConfig
|
||||
}
|
||||
aliceSession2.cryptoService().keysBackupService().checkAndStartKeysBackup()
|
||||
// Test check: aliceSession2 has no keys at login
|
||||
Assert.assertEquals(0, aliceSession2.cryptoService().inboundGroupSessionsCount(false))
|
||||
|
||||
@ -116,6 +124,7 @@ internal class KeysBackupTestHelper(
|
||||
val keysVersion = testHelper.waitForCallback<KeysVersion> {
|
||||
keysBackup.createKeysBackupVersion(megolmBackupCreationInfo, it)
|
||||
}
|
||||
keysBackup.saveBackupRecoveryKey(megolmBackupCreationInfo.recoveryKey, version = keysVersion.version)
|
||||
|
||||
Assert.assertNotNull("Key backup version should not be null", keysVersion.version)
|
||||
|
||||
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.internal.crypto.keysbackup
|
||||
|
||||
import androidx.test.filters.LargeTest
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_AES_256_BACKUP
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeyBackupConfig
|
||||
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
@LargeTest
|
||||
class SymmetricKeysBackupTest : KeysBackupTest() {
|
||||
|
||||
override var keyBackupConfig: KeyBackupConfig? = KeyBackupConfig(
|
||||
defaultAlgorithm = MXCRYPTO_ALGORITHM_AES_256_BACKUP,
|
||||
supportedAlgorithms = listOf(MXCRYPTO_ALGORITHM_AES_256_BACKUP)
|
||||
)
|
||||
}
|
@ -33,6 +33,7 @@ const val MXCRYPTO_ALGORITHM_CURVE_25519_BACKUP = "m.megolm_backup.v1.curve25519
|
||||
|
||||
/**
|
||||
* Matrix algorithm value for AES-256 megolm keys backup.
|
||||
* Symmetric megolm backup
|
||||
*/
|
||||
const val MXCRYPTO_ALGORITHM_AES_256_BACKUP = "org.matrix.msc3270.v1.aes-hmac-sha2"
|
||||
|
||||
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.session.crypto.keysbackup
|
||||
|
||||
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_CURVE_25519_BACKUP
|
||||
|
||||
data class KeyBackupConfig(
|
||||
val defaultAlgorithm: String = MXCRYPTO_ALGORITHM_CURVE_25519_BACKUP,
|
||||
val supportedAlgorithms: List<String> = listOf(MXCRYPTO_ALGORITHM_CURVE_25519_BACKUP)
|
||||
) {
|
||||
fun isAlgorithmSupported(alg: String) = supportedAlgorithms.contains(alg)
|
||||
}
|
@ -22,6 +22,13 @@ import org.matrix.android.sdk.api.listeners.StepProgressListener
|
||||
import org.matrix.android.sdk.api.session.crypto.model.ImportRoomKeysResult
|
||||
|
||||
interface KeysBackupService {
|
||||
|
||||
/**
|
||||
* Define the backup internal configuration, supported algorithm, default algorithm.
|
||||
* @param keyBackupConfig the config
|
||||
*/
|
||||
var keyBackupConfig: KeyBackupConfig
|
||||
|
||||
/**
|
||||
* Retrieve the current version of the backup from the homeserver.
|
||||
*
|
||||
|
@ -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.listeners.ProgressListener
|
||||
import org.matrix.android.sdk.api.listeners.StepProgressListener
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeyBackupConfig
|
||||
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.KeysBackupState
|
||||
@ -87,6 +88,7 @@ import org.matrix.android.sdk.internal.task.configureWith
|
||||
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
|
||||
import org.matrix.olm.OlmException
|
||||
import timber.log.Timber
|
||||
import java.security.InvalidParameterException
|
||||
import javax.inject.Inject
|
||||
import kotlin.random.Random
|
||||
|
||||
@ -128,6 +130,11 @@ internal class DefaultKeysBackupService @Inject constructor(
|
||||
private val uiHandler: Handler,
|
||||
) : KeysBackupService {
|
||||
|
||||
override var keyBackupConfig = KeyBackupConfig(
|
||||
defaultAlgorithm = MXCRYPTO_ALGORITHM_CURVE_25519_BACKUP,
|
||||
supportedAlgorithms = listOf(MXCRYPTO_ALGORITHM_CURVE_25519_BACKUP)
|
||||
)
|
||||
|
||||
// The backup version
|
||||
override var keysBackupVersion: KeysVersionResult? = null
|
||||
private set
|
||||
@ -160,9 +167,10 @@ internal class DefaultKeysBackupService @Inject constructor(
|
||||
) {
|
||||
cryptoCoroutineScope.launch {
|
||||
prepareKeysBackup(
|
||||
algorithm = algorithm ?: DEFAULT_ALGORITHM,
|
||||
algorithm = algorithm ?: keyBackupConfig.defaultAlgorithm,
|
||||
password = password,
|
||||
progressListener = progressListener,
|
||||
config = keyBackupConfig,
|
||||
callback = callback
|
||||
)
|
||||
}
|
||||
@ -172,6 +180,9 @@ internal class DefaultKeysBackupService @Inject constructor(
|
||||
keysBackupCreationInfo: MegolmBackupCreationInfo,
|
||||
callback: MatrixCallback<KeysVersion>
|
||||
) {
|
||||
if (!keyBackupConfig.isAlgorithmSupported(keysBackupCreationInfo.algorithm)) return Unit.also {
|
||||
callback.onFailure(IllegalArgumentException("Unsupported algorithm"))
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val createKeysBackupVersionBody = CreateKeysBackupVersionBody(
|
||||
algorithm = keysBackupCreationInfo.algorithm,
|
||||
@ -364,6 +375,10 @@ internal class DefaultKeysBackupService @Inject constructor(
|
||||
private fun getKeysBackupTrustBg(keysBackupVersion: KeysVersionResult): KeysBackupVersionTrust {
|
||||
val authData = keysBackupVersion.getAuthDataAsMegolmBackupAuthData()
|
||||
|
||||
if (!keyBackupConfig.isAlgorithmSupported(keysBackupVersion.algorithm)) {
|
||||
return KeysBackupVersionTrust(usable = false)
|
||||
}
|
||||
|
||||
if (authData == null || authData.signatures.isNullOrEmpty()) {
|
||||
Timber.v("getKeysBackupTrust: Key backup is absent or missing required data")
|
||||
return KeysBackupVersionTrust(usable = false)
|
||||
@ -453,6 +468,14 @@ internal class DefaultKeysBackupService @Inject constructor(
|
||||
) {
|
||||
Timber.v("trustKeyBackupVersion: $trust, version ${keysBackupVersion.version}")
|
||||
|
||||
if (!keyBackupConfig.isAlgorithmSupported(keysBackupVersion.algorithm)) {
|
||||
Timber.w("trustKeyBackupVersion:trust unsupported algorithm ${keysBackupVersion.algorithm}")
|
||||
uiHandler.post {
|
||||
callback.onFailure(IllegalArgumentException("Missing element"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Get auth data to update it
|
||||
val authData = keysBackupVersion.getValidAuthData()
|
||||
|
||||
@ -621,23 +644,33 @@ internal class DefaultKeysBackupService @Inject constructor(
|
||||
stepProgressListener: StepProgressListener?,
|
||||
callback: MatrixCallback<ImportRoomKeysResult>
|
||||
) {
|
||||
Timber.v("restoreKeysWithRecoveryKey: From backup version: ${keysVersionResult.version}")
|
||||
Timber.v("restoreKeysWithRecoveryKey: From backup version: ${keysVersionResult.version} alg:${keysVersionResult.algorithm}")
|
||||
|
||||
cryptoCoroutineScope.launch(coroutineDispatchers.io) {
|
||||
runCatching {
|
||||
if (!keyBackupConfig.isAlgorithmSupported(keysVersionResult.algorithm)) {
|
||||
throw IllegalArgumentException("Unsupported algorithm")
|
||||
}
|
||||
val backupAlgorithm = algorithmFactory.create(keysVersionResult)
|
||||
val privateKey = extractCurveKeyFromRecoveryKey(recoveryKey)
|
||||
if (privateKey == null || !backupAlgorithm.keyMatches(privateKey)) {
|
||||
Timber.e("restoreKeysWithRecoveryKey: Invalid recovery key for this keys version")
|
||||
throw InvalidParameterException("Invalid recovery key")
|
||||
}
|
||||
// Save for next time and for gossiping
|
||||
// Save now as it's valid, don't wait for the import as it could take long.
|
||||
backupAlgorithm.setPrivateKey(privateKey)
|
||||
saveBackupRecoveryKey(recoveryKey, keysVersionResult.version)
|
||||
stepProgressListener?.onStepProgress(StepProgressListener.Step.DownloadingKey)
|
||||
|
||||
// Get backed up keys from the homeserver
|
||||
val data = getKeys(sessionId, roomId, keysVersionResult.version)
|
||||
extractCurveKeyFromRecoveryKey(recoveryKey)?.also { privateKey ->
|
||||
algorithm?.setPrivateKey(privateKey)
|
||||
backupAlgorithm.setPrivateKey(privateKey)
|
||||
}
|
||||
val sessionsData = withContext(coroutineDispatchers.computation) {
|
||||
algorithm?.decryptSessions(data)
|
||||
}.orEmpty()
|
||||
backupAlgorithm.decryptSessions(data)
|
||||
}
|
||||
// Do not trigger a backup for them if they come from the backup version we are using
|
||||
val backUp = keysVersionResult.version != keysBackupVersion?.version
|
||||
if (backUp) {
|
||||
@ -1002,6 +1035,10 @@ internal class DefaultKeysBackupService @Inject constructor(
|
||||
@WorkerThread
|
||||
private fun isValidRecoveryKeyForKeysBackupVersion(recoveryKey: String, keysBackupData: KeysVersionResult): Boolean {
|
||||
return try {
|
||||
if (!keyBackupConfig.isAlgorithmSupported(keysBackupData.algorithm)) {
|
||||
Timber.w("isValidRecoveryKeyForKeysBackupVersion: unsupported algorithm ${keysBackupData.algorithm}")
|
||||
return false
|
||||
}
|
||||
val algorithm = algorithmFactory.create(keysBackupData)
|
||||
val privateKey = extractCurveKeyFromRecoveryKey(recoveryKey) ?: return false
|
||||
val isValid = algorithm.keyMatches(privateKey)
|
||||
@ -1040,7 +1077,11 @@ internal class DefaultKeysBackupService @Inject constructor(
|
||||
*/
|
||||
private fun enableKeysBackup(keysVersionResult: KeysVersionResult) {
|
||||
val retrievedMegolmBackupAuthData = keysVersionResult.getAuthDataAsMegolmBackupAuthData()
|
||||
|
||||
if (!keyBackupConfig.isAlgorithmSupported(keysVersionResult.algorithm)) {
|
||||
Timber.w("enableKeysBackup: unsupported algorithm ${keysVersionResult.algorithm}")
|
||||
keysBackupStateManager.state = KeysBackupState.Disabled
|
||||
return
|
||||
}
|
||||
if (retrievedMegolmBackupAuthData != null) {
|
||||
keysBackupVersion = keysVersionResult
|
||||
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||
@ -1115,6 +1156,12 @@ internal class DefaultKeysBackupService @Inject constructor(
|
||||
Timber.v("backupKeys: Invalid state: ${getState()}")
|
||||
return
|
||||
}
|
||||
val recoveryKey = cryptoStore.getKeyBackupRecoveryKeyInfo()?.recoveryKey
|
||||
extractCurveKeyFromRecoveryKey(recoveryKey)?.also { privateKey ->
|
||||
// symmetric algorithm need private key to backup :/
|
||||
// a bit hugly, but all this code needs refactoring
|
||||
algorithm?.setPrivateKey(privateKey)
|
||||
}
|
||||
|
||||
// Get a chunk of keys to backup
|
||||
val olmInboundGroupSessionWrappers = cryptoStore.inboundGroupSessionsToBackup(KEY_BACKUP_SEND_KEYS_MAX_COUNT)
|
||||
|
@ -21,17 +21,23 @@ import kotlinx.coroutines.withContext
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||
import org.matrix.android.sdk.api.auth.data.Credentials
|
||||
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_AES_256_BACKUP
|
||||
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_CURVE_25519_BACKUP
|
||||
import org.matrix.android.sdk.api.listeners.ProgressListener
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeyBackupConfig
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.MegolmBackupAes256AuthData
|
||||
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.MegolmBackupCurve25519AuthData
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.computeRecoveryKey
|
||||
import org.matrix.android.sdk.api.util.toBase64NoPadding
|
||||
import org.matrix.android.sdk.internal.crypto.ObjectSigner
|
||||
import org.matrix.android.sdk.internal.crypto.crosssigning.CrossSigningOlm
|
||||
import org.matrix.android.sdk.internal.crypto.keysbackup.model.SignalableMegolmBackupAuthData
|
||||
import org.matrix.android.sdk.internal.crypto.tools.AesHmacSha2
|
||||
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
|
||||
import org.matrix.olm.OlmPkDecryption
|
||||
import timber.log.Timber
|
||||
import java.security.SecureRandom
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class PrepareKeysBackupUseCase @Inject constructor(
|
||||
@ -46,13 +52,15 @@ internal class PrepareKeysBackupUseCase @Inject constructor(
|
||||
algorithm: String,
|
||||
password: String?,
|
||||
progressListener: ProgressListener?,
|
||||
config: KeyBackupConfig,
|
||||
callback: MatrixCallback<MegolmBackupCreationInfo>
|
||||
) = withContext(coroutineDispatchers.io) {
|
||||
if (!config.isAlgorithmSupported(algorithm)) return@withContext Unit.also {
|
||||
callback.onFailure(IllegalArgumentException("Unsupported algorithm"))
|
||||
}
|
||||
when (algorithm) {
|
||||
MXCRYPTO_ALGORITHM_CURVE_25519_BACKUP -> prepareCurve(password, progressListener, callback)
|
||||
/*
|
||||
MXCRYPTO_ALGORITHM_AES_256_BACKUP -> prepareAES(password, progressListener, callback)
|
||||
*/
|
||||
else -> {
|
||||
callback.onFailure(IllegalStateException("Unknown algorithm"))
|
||||
}
|
||||
@ -62,7 +70,7 @@ internal class PrepareKeysBackupUseCase @Inject constructor(
|
||||
private fun prepareCurve(password: String?, progressListener: ProgressListener?, callback: MatrixCallback<MegolmBackupCreationInfo>) {
|
||||
val olmPkDecryption = OlmPkDecryption()
|
||||
try {
|
||||
val signalableMegolmBackupAuthData = if (password != null) {
|
||||
val megolmBackupAuthData = if (password != null) {
|
||||
// Generate a private key from the password
|
||||
val backgroundProgressListener = if (progressListener == null) {
|
||||
null
|
||||
@ -80,40 +88,20 @@ internal class PrepareKeysBackupUseCase @Inject constructor(
|
||||
}
|
||||
}
|
||||
val generatePrivateKeyResult = generatePrivateKeyWithPassword(password, backgroundProgressListener)
|
||||
SignalableMegolmBackupAuthData(
|
||||
MegolmBackupCurve25519AuthData(
|
||||
publicKey = olmPkDecryption.setPrivateKey(generatePrivateKeyResult.privateKey),
|
||||
privateKeySalt = generatePrivateKeyResult.salt,
|
||||
privateKeyIterations = generatePrivateKeyResult.iterations
|
||||
)
|
||||
} else {
|
||||
val publicKey = olmPkDecryption.generateKey()
|
||||
SignalableMegolmBackupAuthData(
|
||||
MegolmBackupCurve25519AuthData(
|
||||
publicKey = publicKey
|
||||
)
|
||||
}
|
||||
val signatures = signKeyBackup(megolmBackupAuthData)
|
||||
|
||||
val canonicalJson = JsonCanonicalizer.getCanonicalJson(Map::class.java, signalableMegolmBackupAuthData.signalableJSONDictionary())
|
||||
|
||||
val signatures = mutableMapOf<String, MutableMap<String, String>>()
|
||||
|
||||
val deviceSignature = objectSigner.signObject(canonicalJson)
|
||||
deviceSignature.forEach { (userID, content) ->
|
||||
signatures[userID] = content.toMutableMap()
|
||||
}
|
||||
|
||||
// If we have cross signing add signature, will throw if cross signing not properly configured
|
||||
try {
|
||||
val crossSign = crossSigningOlm.signObject(CrossSigningOlm.KeyType.MASTER, canonicalJson)
|
||||
signatures[credentials.userId]?.putAll(crossSign)
|
||||
} catch (failure: Throwable) {
|
||||
// ignore and log
|
||||
Timber.w(failure, "prepareKeysBackupVersion: failed to sign with cross signing keys")
|
||||
}
|
||||
|
||||
val signedMegolmBackupCurve25519AuthData = MegolmBackupCurve25519AuthData(
|
||||
publicKey = signalableMegolmBackupAuthData.publicKey,
|
||||
privateKeySalt = signalableMegolmBackupAuthData.privateKeySalt,
|
||||
privateKeyIterations = signalableMegolmBackupAuthData.privateKeyIterations,
|
||||
val signedMegolmBackupCurve25519AuthData = megolmBackupAuthData.copy(
|
||||
signatures = signatures
|
||||
)
|
||||
val creationInfo = MegolmBackupCreationInfo(
|
||||
@ -133,9 +121,65 @@ internal class PrepareKeysBackupUseCase @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
private fun prepareAES(password: String?, progressListener: ProgressListener?, callback: MatrixCallback<MegolmBackupCreationInfo>) {
|
||||
private fun signKeyBackup(authData: MegolmBackupAuthData): MutableMap<String, MutableMap<String, String>> {
|
||||
val canonicalJson = JsonCanonicalizer.getCanonicalJson(Map::class.java, authData.toSignalableJsonDict())
|
||||
val signatures = mutableMapOf<String, MutableMap<String, String>>()
|
||||
|
||||
val deviceSignature = objectSigner.signObject(canonicalJson)
|
||||
deviceSignature.forEach { (userID, content) ->
|
||||
signatures[userID] = content.toMutableMap()
|
||||
}
|
||||
|
||||
// If we have cross signing add signature, will throw if cross signing not properly configured
|
||||
try {
|
||||
val crossSign = crossSigningOlm.signObject(CrossSigningOlm.KeyType.MASTER, canonicalJson)
|
||||
signatures[credentials.userId]?.putAll(crossSign)
|
||||
} catch (failure: Throwable) {
|
||||
// ignore and log
|
||||
Timber.w(failure, "prepareKeysBackupVersion: failed to sign with cross signing keys")
|
||||
}
|
||||
return signatures
|
||||
}
|
||||
|
||||
*/
|
||||
private fun prepareAES(password: String?, progressListener: ProgressListener?, callback: MatrixCallback<MegolmBackupCreationInfo>) {
|
||||
try {
|
||||
val privateKey: ByteArray
|
||||
val authData: MegolmBackupAes256AuthData
|
||||
if (password == null) {
|
||||
privateKey = ByteArray(32).also {
|
||||
SecureRandom().nextBytes(it)
|
||||
}
|
||||
authData = MegolmBackupAes256AuthData()
|
||||
} else {
|
||||
val result = generatePrivateKeyWithPassword(password, progressListener)
|
||||
privateKey = result.privateKey
|
||||
authData = MegolmBackupAes256AuthData(
|
||||
privateKeySalt = result.salt,
|
||||
privateKeyIterations = result.iterations
|
||||
)
|
||||
}
|
||||
val encInfo = AesHmacSha2.calculateKeyCheck(privateKey, null)
|
||||
val authDataWithKeyCheck = authData.copy(
|
||||
iv = encInfo.initializationVector.toBase64NoPadding(),
|
||||
mac = encInfo.mac.toBase64NoPadding()
|
||||
)
|
||||
|
||||
val signatures = signKeyBackup(authDataWithKeyCheck)
|
||||
val creationInfo = MegolmBackupCreationInfo(
|
||||
algorithm = MXCRYPTO_ALGORITHM_AES_256_BACKUP,
|
||||
authData = authDataWithKeyCheck.copy(
|
||||
signatures = signatures
|
||||
),
|
||||
recoveryKey = computeRecoveryKey(privateKey)
|
||||
)
|
||||
|
||||
uiHandler.post {
|
||||
callback.onSuccess(creationInfo)
|
||||
}
|
||||
} catch (failure: Throwable) {
|
||||
uiHandler.post {
|
||||
callback.onFailure(failure)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_AES_256_BACKUP
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysVersionResult
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.MegolmBackupAes256AuthData
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.MegolmBackupAuthData
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.extractCurveKeyFromRecoveryKey
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
import org.matrix.android.sdk.api.util.fromBase64
|
||||
import org.matrix.android.sdk.api.util.toBase64NoPadding
|
||||
@ -96,7 +95,6 @@ internal class KeysBackupAes256Algorithm(keysVersions: KeysVersionResult) : Keys
|
||||
}
|
||||
|
||||
private fun decryptSession(sessionData: JsonDict, sessionId: String, roomId: String, privateKey: ByteArray): MegolmSessionData? {
|
||||
|
||||
val cipherRawBytes = sessionData["ciphertext"]?.toString()?.fromBase64() ?: return null
|
||||
val mac = sessionData["mac"]?.toString()?.fromBase64() ?: throw IllegalStateException("Bad mac")
|
||||
val iv = sessionData["iv"]?.toString()?.fromBase64() ?: ByteArray(16)
|
||||
|
@ -59,4 +59,3 @@ internal fun MegolmSessionData.asBackupJson(): String {
|
||||
.adapter(Map::class.java)
|
||||
.toJson(sessionBackupData)
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,6 @@ import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_CURVE_25519_BACKUP
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysVersionResult
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.MegolmBackupAuthData
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.MegolmBackupCurve25519AuthData
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.extractCurveKeyFromRecoveryKey
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
import org.matrix.android.sdk.internal.crypto.MegolmSessionData
|
||||
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysBackupData
|
||||
@ -116,7 +115,6 @@ internal class KeysBackupCurve25519Algorithm(keysVersions: KeysVersionResult) :
|
||||
}
|
||||
|
||||
private fun decryptSession(sessionData: JsonDict, sessionId: String, roomId: String, decryption: OlmPkDecryption): MegolmSessionData? {
|
||||
|
||||
val ciphertext = sessionData["ciphertext"]?.toString()
|
||||
val mac = sessionData["mac"]?.toString()
|
||||
val ephemeralKey = sessionData["ephemeral"]?.toString()
|
||||
|
@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.crypto.keysbackup.model
|
||||
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
|
||||
internal data class SignalableMegolmBackupAuthData(
|
||||
val publicKey: String,
|
||||
val privateKeySalt: String? = null,
|
||||
val privateKeyIterations: Int? = null
|
||||
) {
|
||||
fun signalableJSONDictionary(): JsonDict = HashMap<String, Any>().apply {
|
||||
put("public_key", publicKey)
|
||||
|
||||
privateKeySalt?.let {
|
||||
put("private_key_salt", it)
|
||||
}
|
||||
privateKeyIterations?.let {
|
||||
put("private_key_iterations", it)
|
||||
}
|
||||
}
|
||||
}
|
@ -148,5 +148,4 @@ internal object AesHmacSha2 {
|
||||
}
|
||||
|
||||
private fun zeroByteArray(size: Int): ByteArray = ByteArray(size) { 0.toByte() }
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user