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.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.crosssigning.DeviceTrustLevel
|
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.KeysBackupLastVersionResult
|
||||||
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
|
||||||
@ -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.keysbackup.toKeysVersionResult
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.ImportRoomKeysResult
|
import org.matrix.android.sdk.api.session.crypto.model.ImportRoomKeysResult
|
||||||
import org.matrix.android.sdk.api.session.getRoom
|
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.runCryptoTest
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
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.RetryTestRule
|
||||||
|
import org.matrix.android.sdk.common.SessionTestParams
|
||||||
import org.matrix.android.sdk.common.TestConstants
|
import org.matrix.android.sdk.common.TestConstants
|
||||||
import org.matrix.android.sdk.common.waitFor
|
import org.matrix.android.sdk.common.waitFor
|
||||||
import org.matrix.android.sdk.internal.crypto.keysbackup.algorithm.KeysBackupAlgorithmFactory
|
import org.matrix.android.sdk.internal.crypto.keysbackup.algorithm.KeysBackupAlgorithmFactory
|
||||||
@ -63,10 +67,21 @@ import kotlin.coroutines.resume
|
|||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@FixMethodOrder(MethodSorters.JVM)
|
@FixMethodOrder(MethodSorters.JVM)
|
||||||
@LargeTest
|
@LargeTest
|
||||||
class KeysBackupTest : InstrumentedTest {
|
open class KeysBackupTest : InstrumentedTest {
|
||||||
|
|
||||||
@get:Rule val rule = RetryTestRule(3)
|
@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
|
* - From doE2ETestWithAliceAndBobInARoomWithEncryptedMessages, we should have no backed up keys
|
||||||
* - Check backup keys after having marked one as backed up
|
* - Check backup keys after having marked one as backed up
|
||||||
@ -207,7 +222,7 @@ class KeysBackupTest : InstrumentedTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun backupAfterCreateKeysBackupVersionTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
fun backupAfterCreateKeysBackupVersionTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||||
|
|
||||||
@ -248,7 +263,7 @@ class KeysBackupTest : InstrumentedTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun backupAllGroupSessionsTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
fun backupAllGroupSessionsTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||||
|
|
||||||
@ -293,7 +308,7 @@ class KeysBackupTest : InstrumentedTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testEncryptAndDecryptKeysBackupData() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
fun testEncryptAndDecryptKeysBackupData() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||||
|
|
||||||
@ -342,7 +357,7 @@ class KeysBackupTest : InstrumentedTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun restoreKeysBackupTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
fun restoreKeysBackupTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
||||||
|
|
||||||
@ -428,7 +443,7 @@ class KeysBackupTest : InstrumentedTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun trustKeyBackupVersionTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
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
|
// - Do an e2e backup to the homeserver with a recovery key
|
||||||
// - And log Alice on a new device
|
// - And log Alice on a new device
|
||||||
@ -488,7 +503,7 @@ class KeysBackupTest : InstrumentedTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun trustKeyBackupVersionWithRecoveryKeyTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
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
|
// - Do an e2e backup to the homeserver with a recovery key
|
||||||
// - And log Alice on a new device
|
// - And log Alice on a new device
|
||||||
@ -546,7 +561,7 @@ class KeysBackupTest : InstrumentedTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun trustKeyBackupVersionWithWrongRecoveryKeyTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
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
|
// - Do an e2e backup to the homeserver with a recovery key
|
||||||
// - And log Alice on a new device
|
// - And log Alice on a new device
|
||||||
@ -588,7 +603,7 @@ class KeysBackupTest : InstrumentedTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun trustKeyBackupVersionWithPasswordTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
fun trustKeyBackupVersionWithPasswordTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
val password = "Password"
|
val password = "Password"
|
||||||
|
|
||||||
@ -648,7 +663,7 @@ class KeysBackupTest : InstrumentedTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun trustKeyBackupVersionWithWrongPasswordTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
fun trustKeyBackupVersionWithWrongPasswordTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
val password = "Password"
|
val password = "Password"
|
||||||
val badPassword = "Bad Password"
|
val badPassword = "Bad Password"
|
||||||
@ -689,7 +704,7 @@ class KeysBackupTest : InstrumentedTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun restoreKeysBackupWithAWrongRecoveryKeyTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
fun restoreKeysBackupWithAWrongRecoveryKeyTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
||||||
val keysBackupService = testData.aliceSession2.cryptoService().keysBackupService()
|
val keysBackupService = testData.aliceSession2.cryptoService().keysBackupService()
|
||||||
@ -717,7 +732,7 @@ class KeysBackupTest : InstrumentedTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testBackupWithPassword() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
fun testBackupWithPassword() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
val password = "password"
|
val password = "password"
|
||||||
|
|
||||||
@ -773,7 +788,7 @@ class KeysBackupTest : InstrumentedTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun restoreKeysBackupWithAWrongPasswordTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
fun restoreKeysBackupWithAWrongPasswordTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
val password = "password"
|
val password = "password"
|
||||||
val wrongPassword = "passw0rd"
|
val wrongPassword = "passw0rd"
|
||||||
@ -804,7 +819,7 @@ class KeysBackupTest : InstrumentedTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testUseRecoveryKeyToRestoreAPasswordBasedKeysBackup() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
fun testUseRecoveryKeyToRestoreAPasswordBasedKeysBackup() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
val password = "password"
|
val password = "password"
|
||||||
|
|
||||||
@ -833,7 +848,7 @@ class KeysBackupTest : InstrumentedTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testUsePasswordToRestoreARecoveryKeyBasedKeysBackup() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
fun testUsePasswordToRestoreARecoveryKeyBasedKeysBackup() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
||||||
val keysBackupService = testData.aliceSession2.cryptoService().keysBackupService()
|
val keysBackupService = testData.aliceSession2.cryptoService().keysBackupService()
|
||||||
@ -859,7 +874,7 @@ class KeysBackupTest : InstrumentedTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testIsKeysBackupTrusted() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
fun testIsKeysBackupTrusted() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
// - Create a backup version
|
// - Create a backup version
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||||
@ -904,7 +919,7 @@ class KeysBackupTest : InstrumentedTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testBackupWhenAnotherBackupWasCreated() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
fun testBackupWhenAnotherBackupWasCreated() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
// - Create a backup version
|
// - Create a backup version
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||||
@ -979,7 +994,7 @@ class KeysBackupTest : InstrumentedTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testBackupAfterVerifyingADevice() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
fun testBackupAfterVerifyingADevice() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
// - Create a backup version
|
// - Create a backup version
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||||
@ -1066,7 +1081,7 @@ class KeysBackupTest : InstrumentedTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun deleteKeysBackupTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
fun deleteKeysBackupTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = createKeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
// - Create a backup version
|
// - Create a backup version
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||||
@ -1089,4 +1104,9 @@ class KeysBackupTest : InstrumentedTest {
|
|||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
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.junit.Assert
|
||||||
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.keysbackup.KeyBackupConfig
|
||||||
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
|
||||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupStateListener
|
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupStateListener
|
||||||
@ -34,7 +35,8 @@ import kotlin.coroutines.resume
|
|||||||
|
|
||||||
internal class KeysBackupTestHelper(
|
internal class KeysBackupTestHelper(
|
||||||
private val testHelper: CommonTestHelper,
|
private val testHelper: CommonTestHelper,
|
||||||
private val cryptoTestHelper: CryptoTestHelper
|
private val cryptoTestHelper: CryptoTestHelper,
|
||||||
|
private val keyBackupConfig: KeyBackupConfig? = null
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun waitForKeybackUpBatching() {
|
fun waitForKeybackUpBatching() {
|
||||||
@ -55,6 +57,9 @@ internal class KeysBackupTestHelper(
|
|||||||
|
|
||||||
val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService).store
|
val cryptoStore = (cryptoTestData.firstSession.cryptoService().keysBackupService() as DefaultKeysBackupService).store
|
||||||
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
|
val keysBackup = cryptoTestData.firstSession.cryptoService().keysBackupService()
|
||||||
|
if (keyBackupConfig != null) {
|
||||||
|
keysBackup.keyBackupConfig = keyBackupConfig
|
||||||
|
}
|
||||||
|
|
||||||
val stateObserver = StateObserver(keysBackup)
|
val stateObserver = StateObserver(keysBackup)
|
||||||
|
|
||||||
@ -81,7 +86,10 @@ internal class KeysBackupTestHelper(
|
|||||||
|
|
||||||
// - Log Alice on a new device
|
// - Log Alice on a new device
|
||||||
val aliceSession2 = testHelper.logIntoAccount(aliceUserId, KeysBackupTestConstants.defaultSessionParamsWithInitialSync)
|
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
|
// Test check: aliceSession2 has no keys at login
|
||||||
Assert.assertEquals(0, aliceSession2.cryptoService().inboundGroupSessionsCount(false))
|
Assert.assertEquals(0, aliceSession2.cryptoService().inboundGroupSessionsCount(false))
|
||||||
|
|
||||||
@ -116,6 +124,7 @@ internal class KeysBackupTestHelper(
|
|||||||
val keysVersion = testHelper.waitForCallback<KeysVersion> {
|
val keysVersion = testHelper.waitForCallback<KeysVersion> {
|
||||||
keysBackup.createKeysBackupVersion(megolmBackupCreationInfo, it)
|
keysBackup.createKeysBackupVersion(megolmBackupCreationInfo, it)
|
||||||
}
|
}
|
||||||
|
keysBackup.saveBackupRecoveryKey(megolmBackupCreationInfo.recoveryKey, version = keysVersion.version)
|
||||||
|
|
||||||
Assert.assertNotNull("Key backup version should not be null", 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.
|
* 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"
|
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
|
import org.matrix.android.sdk.api.session.crypto.model.ImportRoomKeysResult
|
||||||
|
|
||||||
interface KeysBackupService {
|
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.
|
* 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.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.KeyBackupConfig
|
||||||
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
|
||||||
@ -87,6 +88,7 @@ import org.matrix.android.sdk.internal.task.configureWith
|
|||||||
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 java.security.InvalidParameterException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@ -128,6 +130,11 @@ internal class DefaultKeysBackupService @Inject constructor(
|
|||||||
private val uiHandler: Handler,
|
private val uiHandler: Handler,
|
||||||
) : KeysBackupService {
|
) : KeysBackupService {
|
||||||
|
|
||||||
|
override var keyBackupConfig = KeyBackupConfig(
|
||||||
|
defaultAlgorithm = MXCRYPTO_ALGORITHM_CURVE_25519_BACKUP,
|
||||||
|
supportedAlgorithms = listOf(MXCRYPTO_ALGORITHM_CURVE_25519_BACKUP)
|
||||||
|
)
|
||||||
|
|
||||||
// The backup version
|
// The backup version
|
||||||
override var keysBackupVersion: KeysVersionResult? = null
|
override var keysBackupVersion: KeysVersionResult? = null
|
||||||
private set
|
private set
|
||||||
@ -160,9 +167,10 @@ internal class DefaultKeysBackupService @Inject constructor(
|
|||||||
) {
|
) {
|
||||||
cryptoCoroutineScope.launch {
|
cryptoCoroutineScope.launch {
|
||||||
prepareKeysBackup(
|
prepareKeysBackup(
|
||||||
algorithm = algorithm ?: DEFAULT_ALGORITHM,
|
algorithm = algorithm ?: keyBackupConfig.defaultAlgorithm,
|
||||||
password = password,
|
password = password,
|
||||||
progressListener = progressListener,
|
progressListener = progressListener,
|
||||||
|
config = keyBackupConfig,
|
||||||
callback = callback
|
callback = callback
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -172,6 +180,9 @@ internal class DefaultKeysBackupService @Inject constructor(
|
|||||||
keysBackupCreationInfo: MegolmBackupCreationInfo,
|
keysBackupCreationInfo: MegolmBackupCreationInfo,
|
||||||
callback: MatrixCallback<KeysVersion>
|
callback: MatrixCallback<KeysVersion>
|
||||||
) {
|
) {
|
||||||
|
if (!keyBackupConfig.isAlgorithmSupported(keysBackupCreationInfo.algorithm)) return Unit.also {
|
||||||
|
callback.onFailure(IllegalArgumentException("Unsupported algorithm"))
|
||||||
|
}
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val createKeysBackupVersionBody = CreateKeysBackupVersionBody(
|
val createKeysBackupVersionBody = CreateKeysBackupVersionBody(
|
||||||
algorithm = keysBackupCreationInfo.algorithm,
|
algorithm = keysBackupCreationInfo.algorithm,
|
||||||
@ -364,6 +375,10 @@ internal class DefaultKeysBackupService @Inject constructor(
|
|||||||
private fun getKeysBackupTrustBg(keysBackupVersion: KeysVersionResult): KeysBackupVersionTrust {
|
private fun getKeysBackupTrustBg(keysBackupVersion: KeysVersionResult): KeysBackupVersionTrust {
|
||||||
val authData = keysBackupVersion.getAuthDataAsMegolmBackupAuthData()
|
val authData = keysBackupVersion.getAuthDataAsMegolmBackupAuthData()
|
||||||
|
|
||||||
|
if (!keyBackupConfig.isAlgorithmSupported(keysBackupVersion.algorithm)) {
|
||||||
|
return KeysBackupVersionTrust(usable = false)
|
||||||
|
}
|
||||||
|
|
||||||
if (authData == null || authData.signatures.isNullOrEmpty()) {
|
if (authData == null || authData.signatures.isNullOrEmpty()) {
|
||||||
Timber.v("getKeysBackupTrust: Key backup is absent or missing required data")
|
Timber.v("getKeysBackupTrust: Key backup is absent or missing required data")
|
||||||
return KeysBackupVersionTrust(usable = false)
|
return KeysBackupVersionTrust(usable = false)
|
||||||
@ -453,6 +468,14 @@ internal class DefaultKeysBackupService @Inject constructor(
|
|||||||
) {
|
) {
|
||||||
Timber.v("trustKeyBackupVersion: $trust, version ${keysBackupVersion.version}")
|
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
|
// Get auth data to update it
|
||||||
val authData = keysBackupVersion.getValidAuthData()
|
val authData = keysBackupVersion.getValidAuthData()
|
||||||
|
|
||||||
@ -621,23 +644,33 @@ internal class DefaultKeysBackupService @Inject constructor(
|
|||||||
stepProgressListener: StepProgressListener?,
|
stepProgressListener: StepProgressListener?,
|
||||||
callback: MatrixCallback<ImportRoomKeysResult>
|
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) {
|
cryptoCoroutineScope.launch(coroutineDispatchers.io) {
|
||||||
runCatching {
|
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 for next time and for gossiping
|
||||||
// Save now as it's valid, don't wait for the import as it could take long.
|
// Save now as it's valid, don't wait for the import as it could take long.
|
||||||
|
backupAlgorithm.setPrivateKey(privateKey)
|
||||||
saveBackupRecoveryKey(recoveryKey, keysVersionResult.version)
|
saveBackupRecoveryKey(recoveryKey, keysVersionResult.version)
|
||||||
stepProgressListener?.onStepProgress(StepProgressListener.Step.DownloadingKey)
|
stepProgressListener?.onStepProgress(StepProgressListener.Step.DownloadingKey)
|
||||||
|
|
||||||
// Get backed up keys from the homeserver
|
// Get backed up keys from the homeserver
|
||||||
val data = getKeys(sessionId, roomId, keysVersionResult.version)
|
val data = getKeys(sessionId, roomId, keysVersionResult.version)
|
||||||
extractCurveKeyFromRecoveryKey(recoveryKey)?.also { privateKey ->
|
extractCurveKeyFromRecoveryKey(recoveryKey)?.also { privateKey ->
|
||||||
algorithm?.setPrivateKey(privateKey)
|
backupAlgorithm.setPrivateKey(privateKey)
|
||||||
}
|
}
|
||||||
val sessionsData = withContext(coroutineDispatchers.computation) {
|
val sessionsData = withContext(coroutineDispatchers.computation) {
|
||||||
algorithm?.decryptSessions(data)
|
backupAlgorithm.decryptSessions(data)
|
||||||
}.orEmpty()
|
}
|
||||||
// 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) {
|
||||||
@ -1002,6 +1035,10 @@ internal class DefaultKeysBackupService @Inject constructor(
|
|||||||
@WorkerThread
|
@WorkerThread
|
||||||
private fun isValidRecoveryKeyForKeysBackupVersion(recoveryKey: String, keysBackupData: KeysVersionResult): Boolean {
|
private fun isValidRecoveryKeyForKeysBackupVersion(recoveryKey: String, keysBackupData: KeysVersionResult): Boolean {
|
||||||
return try {
|
return try {
|
||||||
|
if (!keyBackupConfig.isAlgorithmSupported(keysBackupData.algorithm)) {
|
||||||
|
Timber.w("isValidRecoveryKeyForKeysBackupVersion: unsupported algorithm ${keysBackupData.algorithm}")
|
||||||
|
return false
|
||||||
|
}
|
||||||
val algorithm = algorithmFactory.create(keysBackupData)
|
val algorithm = algorithmFactory.create(keysBackupData)
|
||||||
val privateKey = extractCurveKeyFromRecoveryKey(recoveryKey) ?: return false
|
val privateKey = extractCurveKeyFromRecoveryKey(recoveryKey) ?: return false
|
||||||
val isValid = algorithm.keyMatches(privateKey)
|
val isValid = algorithm.keyMatches(privateKey)
|
||||||
@ -1040,7 +1077,11 @@ internal class DefaultKeysBackupService @Inject constructor(
|
|||||||
*/
|
*/
|
||||||
private fun enableKeysBackup(keysVersionResult: KeysVersionResult) {
|
private fun enableKeysBackup(keysVersionResult: KeysVersionResult) {
|
||||||
val retrievedMegolmBackupAuthData = keysVersionResult.getAuthDataAsMegolmBackupAuthData()
|
val retrievedMegolmBackupAuthData = keysVersionResult.getAuthDataAsMegolmBackupAuthData()
|
||||||
|
if (!keyBackupConfig.isAlgorithmSupported(keysVersionResult.algorithm)) {
|
||||||
|
Timber.w("enableKeysBackup: unsupported algorithm ${keysVersionResult.algorithm}")
|
||||||
|
keysBackupStateManager.state = KeysBackupState.Disabled
|
||||||
|
return
|
||||||
|
}
|
||||||
if (retrievedMegolmBackupAuthData != null) {
|
if (retrievedMegolmBackupAuthData != null) {
|
||||||
keysBackupVersion = keysVersionResult
|
keysBackupVersion = keysVersionResult
|
||||||
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||||
@ -1115,6 +1156,12 @@ internal class DefaultKeysBackupService @Inject constructor(
|
|||||||
Timber.v("backupKeys: Invalid state: ${getState()}")
|
Timber.v("backupKeys: Invalid state: ${getState()}")
|
||||||
return
|
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
|
// Get a chunk of keys to backup
|
||||||
val olmInboundGroupSessionWrappers = cryptoStore.inboundGroupSessionsToBackup(KEY_BACKUP_SEND_KEYS_MAX_COUNT)
|
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.MatrixCallback
|
||||||
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||||
import org.matrix.android.sdk.api.auth.data.Credentials
|
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.crypto.MXCRYPTO_ALGORITHM_CURVE_25519_BACKUP
|
||||||
import org.matrix.android.sdk.api.listeners.ProgressListener
|
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.MegolmBackupCreationInfo
|
||||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.MegolmBackupCurve25519AuthData
|
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.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.ObjectSigner
|
||||||
import org.matrix.android.sdk.internal.crypto.crosssigning.CrossSigningOlm
|
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.android.sdk.internal.util.JsonCanonicalizer
|
||||||
import org.matrix.olm.OlmPkDecryption
|
import org.matrix.olm.OlmPkDecryption
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import java.security.SecureRandom
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class PrepareKeysBackupUseCase @Inject constructor(
|
internal class PrepareKeysBackupUseCase @Inject constructor(
|
||||||
@ -46,13 +52,15 @@ internal class PrepareKeysBackupUseCase @Inject constructor(
|
|||||||
algorithm: String,
|
algorithm: String,
|
||||||
password: String?,
|
password: String?,
|
||||||
progressListener: ProgressListener?,
|
progressListener: ProgressListener?,
|
||||||
|
config: KeyBackupConfig,
|
||||||
callback: MatrixCallback<MegolmBackupCreationInfo>
|
callback: MatrixCallback<MegolmBackupCreationInfo>
|
||||||
) = withContext(coroutineDispatchers.io) {
|
) = withContext(coroutineDispatchers.io) {
|
||||||
|
if (!config.isAlgorithmSupported(algorithm)) return@withContext Unit.also {
|
||||||
|
callback.onFailure(IllegalArgumentException("Unsupported algorithm"))
|
||||||
|
}
|
||||||
when (algorithm) {
|
when (algorithm) {
|
||||||
MXCRYPTO_ALGORITHM_CURVE_25519_BACKUP -> prepareCurve(password, progressListener, callback)
|
MXCRYPTO_ALGORITHM_CURVE_25519_BACKUP -> prepareCurve(password, progressListener, callback)
|
||||||
/*
|
|
||||||
MXCRYPTO_ALGORITHM_AES_256_BACKUP -> prepareAES(password, progressListener, callback)
|
MXCRYPTO_ALGORITHM_AES_256_BACKUP -> prepareAES(password, progressListener, callback)
|
||||||
*/
|
|
||||||
else -> {
|
else -> {
|
||||||
callback.onFailure(IllegalStateException("Unknown algorithm"))
|
callback.onFailure(IllegalStateException("Unknown algorithm"))
|
||||||
}
|
}
|
||||||
@ -62,7 +70,7 @@ internal class PrepareKeysBackupUseCase @Inject constructor(
|
|||||||
private fun prepareCurve(password: String?, progressListener: ProgressListener?, callback: MatrixCallback<MegolmBackupCreationInfo>) {
|
private fun prepareCurve(password: String?, progressListener: ProgressListener?, callback: MatrixCallback<MegolmBackupCreationInfo>) {
|
||||||
val olmPkDecryption = OlmPkDecryption()
|
val olmPkDecryption = OlmPkDecryption()
|
||||||
try {
|
try {
|
||||||
val signalableMegolmBackupAuthData = if (password != null) {
|
val megolmBackupAuthData = if (password != null) {
|
||||||
// Generate a private key from the password
|
// Generate a private key from the password
|
||||||
val backgroundProgressListener = if (progressListener == null) {
|
val backgroundProgressListener = if (progressListener == null) {
|
||||||
null
|
null
|
||||||
@ -80,40 +88,20 @@ internal class PrepareKeysBackupUseCase @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val generatePrivateKeyResult = generatePrivateKeyWithPassword(password, backgroundProgressListener)
|
val generatePrivateKeyResult = generatePrivateKeyWithPassword(password, backgroundProgressListener)
|
||||||
SignalableMegolmBackupAuthData(
|
MegolmBackupCurve25519AuthData(
|
||||||
publicKey = olmPkDecryption.setPrivateKey(generatePrivateKeyResult.privateKey),
|
publicKey = olmPkDecryption.setPrivateKey(generatePrivateKeyResult.privateKey),
|
||||||
privateKeySalt = generatePrivateKeyResult.salt,
|
privateKeySalt = generatePrivateKeyResult.salt,
|
||||||
privateKeyIterations = generatePrivateKeyResult.iterations
|
privateKeyIterations = generatePrivateKeyResult.iterations
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
val publicKey = olmPkDecryption.generateKey()
|
val publicKey = olmPkDecryption.generateKey()
|
||||||
SignalableMegolmBackupAuthData(
|
MegolmBackupCurve25519AuthData(
|
||||||
publicKey = publicKey
|
publicKey = publicKey
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
val signatures = signKeyBackup(megolmBackupAuthData)
|
||||||
|
|
||||||
val canonicalJson = JsonCanonicalizer.getCanonicalJson(Map::class.java, signalableMegolmBackupAuthData.signalableJSONDictionary())
|
val signedMegolmBackupCurve25519AuthData = megolmBackupAuthData.copy(
|
||||||
|
|
||||||
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,
|
|
||||||
signatures = signatures
|
signatures = signatures
|
||||||
)
|
)
|
||||||
val creationInfo = MegolmBackupCreationInfo(
|
val creationInfo = MegolmBackupCreationInfo(
|
||||||
@ -133,9 +121,65 @@ internal class PrepareKeysBackupUseCase @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
private fun signKeyBackup(authData: MegolmBackupAuthData): MutableMap<String, MutableMap<String, String>> {
|
||||||
private fun prepareAES(password: String?, progressListener: ProgressListener?, callback: MatrixCallback<MegolmBackupCreationInfo>) {
|
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.KeysVersionResult
|
||||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.MegolmBackupAes256AuthData
|
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.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.JsonDict
|
||||||
import org.matrix.android.sdk.api.util.fromBase64
|
import org.matrix.android.sdk.api.util.fromBase64
|
||||||
import org.matrix.android.sdk.api.util.toBase64NoPadding
|
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? {
|
private fun decryptSession(sessionData: JsonDict, sessionId: String, roomId: String, privateKey: ByteArray): MegolmSessionData? {
|
||||||
|
|
||||||
val cipherRawBytes = sessionData["ciphertext"]?.toString()?.fromBase64() ?: return null
|
val cipherRawBytes = sessionData["ciphertext"]?.toString()?.fromBase64() ?: return null
|
||||||
val mac = sessionData["mac"]?.toString()?.fromBase64() ?: throw IllegalStateException("Bad mac")
|
val mac = sessionData["mac"]?.toString()?.fromBase64() ?: throw IllegalStateException("Bad mac")
|
||||||
val iv = sessionData["iv"]?.toString()?.fromBase64() ?: ByteArray(16)
|
val iv = sessionData["iv"]?.toString()?.fromBase64() ?: ByteArray(16)
|
||||||
|
@ -59,4 +59,3 @@ internal fun MegolmSessionData.asBackupJson(): String {
|
|||||||
.adapter(Map::class.java)
|
.adapter(Map::class.java)
|
||||||
.toJson(sessionBackupData)
|
.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.KeysVersionResult
|
||||||
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.MegolmBackupCurve25519AuthData
|
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.api.util.JsonDict
|
||||||
import org.matrix.android.sdk.internal.crypto.MegolmSessionData
|
import org.matrix.android.sdk.internal.crypto.MegolmSessionData
|
||||||
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysBackupData
|
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? {
|
private fun decryptSession(sessionData: JsonDict, sessionId: String, roomId: String, decryption: OlmPkDecryption): MegolmSessionData? {
|
||||||
|
|
||||||
val ciphertext = sessionData["ciphertext"]?.toString()
|
val ciphertext = sessionData["ciphertext"]?.toString()
|
||||||
val mac = sessionData["mac"]?.toString()
|
val mac = sessionData["mac"]?.toString()
|
||||||
val ephemeralKey = sessionData["ephemeral"]?.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() }
|
private fun zeroByteArray(size: Int): ByteArray = ByteArray(size) { 0.toByte() }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user