mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-24 10:25:51 +03:00
Merge pull request #8366 from vector-im/feature/bca/rust_integration_test_wf
add workflow for rust test
This commit is contained in:
commit
e7c122ef1d
12 changed files with 288 additions and 73 deletions
102
.github/workflows/tests-rust.yml
vendored
Normal file
102
.github/workflows/tests-rust.yml
vendored
Normal file
|
@ -0,0 +1,102 @@
|
|||
name: Test
|
||||
|
||||
on:
|
||||
pull_request: { }
|
||||
push:
|
||||
branches: [ main, develop ]
|
||||
paths-ignore:
|
||||
- '.github/**'
|
||||
|
||||
# Enrich gradle.properties for CI/CD
|
||||
env:
|
||||
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx3072m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.daemon.jvm.options="-Xmx2560m" -Dkotlin.incremental=false
|
||||
CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 4 --no-daemon
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: Runs all tests with rust crypto
|
||||
runs-on: buildjet-4vcpu-ubuntu-2204
|
||||
timeout-minutes: 90 # We might need to increase it if the time for tests grows
|
||||
strategy:
|
||||
matrix:
|
||||
api-level: [28]
|
||||
# Allow all jobs on main and develop. Just one per PR.
|
||||
concurrency:
|
||||
group: ${{ github.ref == 'refs/heads/main' && format('unit-tests-main-rust-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('unit-tests-develop-rust-{0}', github.sha) || format('unit-tests-rust-{0}', github.ref) }}
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
lfs: true
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'adopt'
|
||||
java-version: '11'
|
||||
- uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
|
||||
gradle-home-cache-cleanup: ${{ github.ref == 'refs/heads/develop' }}
|
||||
|
||||
# - name: Run screenshot tests
|
||||
# run: ./gradlew verifyScreenshots $CI_GRADLE_ARG_PROPERTIES
|
||||
|
||||
# - name: Archive Screenshot Results on Error
|
||||
# if: failure()
|
||||
# uses: actions/upload-artifact@v3
|
||||
# with:
|
||||
# name: screenshot-results
|
||||
# path: |
|
||||
# **/out/failures/
|
||||
# **/build/reports/tests/*UnitTest/
|
||||
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.8
|
||||
- uses: michaelkaye/setup-matrix-synapse@v1.0.4
|
||||
with:
|
||||
uploadLogs: true
|
||||
httpPort: 8080
|
||||
disableRateLimiting: true
|
||||
public_baseurl: "http://10.0.2.2:8080/"
|
||||
|
||||
- name: Run all the codecoverage tests at once
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
# continue-on-error: true
|
||||
with:
|
||||
api-level: ${{ matrix.api-level }}
|
||||
arch: x86
|
||||
profile: Nexus 5X
|
||||
target: playstore
|
||||
force-avd-creation: false
|
||||
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
||||
disable-animations: true
|
||||
# emulator-build: 7425822
|
||||
script: |
|
||||
./gradlew gatherGplayRustCryptoDebugStringTemplates $CI_GRADLE_ARG_PROPERTIES
|
||||
./gradlew instrumentationTestsRustWithCoverage $CI_GRADLE_ARG_PROPERTIES
|
||||
./gradlew generateCoverageReport $CI_GRADLE_ARG_PROPERTIES
|
||||
|
||||
- name: Upload Rust Integration Test Report Log
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: integration-test-rust-error-results
|
||||
path: |
|
||||
*/build/outputs/androidTest-results/connected/
|
||||
*/build/reports/androidTests/connected/
|
||||
|
||||
# For now ignore sonar
|
||||
# - name: Publish results to Sonar
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.SONARQUBE_GITHUB_API_TOKEN }} # Needed to get PR information, if any
|
||||
# SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
# ORG_GRADLE_PROJECT_SONAR_LOGIN: ${{ secrets.SONAR_TOKEN }}
|
||||
# if: ${{ always() && env.GITHUB_TOKEN != '' && env.SONAR_TOKEN != '' && env.ORG_GRADLE_PROJECT_SONAR_LOGIN != '' }}
|
||||
# run: ./gradlew sonar $CI_GRADLE_ARG_PROPERTIES
|
||||
|
||||
- name: Format unit test results
|
||||
if: always()
|
||||
run: python3 ./tools/ci/render_test_output.py unit ./**/build/test-results/**/*.xml
|
||||
|
||||
|
1
changelog.d/8366.misc
Normal file
1
changelog.d/8366.misc
Normal file
|
@ -0,0 +1 @@
|
|||
CI: Add workflow to run test with crypto flavor
|
|
@ -89,3 +89,9 @@ task instrumentationTestsWithCoverage(type: GradleBuild) {
|
|||
startParameter.projectProperties['android.testInstrumentationRunnerArguments.notPackage'] = 'im.vector.app.ui'
|
||||
tasks = [':vector-app:connectedGplayKotlinCryptoDebugAndroidTest', ':vector:connectedKotlinCryptoDebugAndroidTest', 'matrix-sdk-android:connectedKotlinCryptoDebugAndroidTest']
|
||||
}
|
||||
|
||||
task instrumentationTestsRustWithCoverage(type: GradleBuild) {
|
||||
startParameter.projectProperties.coverage = "true"
|
||||
startParameter.projectProperties['android.testInstrumentationRunnerArguments.notPackage'] = 'im.vector.app.ui'
|
||||
tasks = [':vector-app:connectedGplayRustCryptoDebugAndroidTest', ':vector:connectedRustCryptoDebugAndroidTest', 'matrix-sdk-android:connectedRustCryptoDebugAndroidTest']
|
||||
}
|
||||
|
|
|
@ -216,7 +216,7 @@ dependencies {
|
|||
|
||||
implementation libs.google.phonenumber
|
||||
|
||||
rustCryptoImplementation("org.matrix.rustcomponents:crypto-android:0.3.5")
|
||||
rustCryptoImplementation("org.matrix.rustcomponents:crypto-android:0.3.7")
|
||||
// rustCryptoApi project(":library:rustCrypto")
|
||||
|
||||
testImplementation libs.tests.junit
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.junit.Assert.assertEquals
|
|||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Assume
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
|
@ -128,6 +129,7 @@ class KeysBackupTest : InstrumentedTest {
|
|||
@Test
|
||||
fun createKeysBackupVersionTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val bobSession = testHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams)
|
||||
Log.d("#E2E", "Initializing crosssigning for ${bobSession.myUserId.take(8)}")
|
||||
cryptoTestHelper.initializeCrossSigning(bobSession)
|
||||
|
||||
val keysBackup = bobSession.cryptoService().keysBackupService()
|
||||
|
@ -136,12 +138,14 @@ class KeysBackupTest : InstrumentedTest {
|
|||
|
||||
assertFalse(keysBackup.isEnabled())
|
||||
|
||||
Log.d("#E2E", "prepareKeysBackupVersion")
|
||||
val megolmBackupCreationInfo =
|
||||
keysBackup.prepareKeysBackupVersion(null, null)
|
||||
|
||||
assertFalse(keysBackup.isEnabled())
|
||||
|
||||
// Create the version
|
||||
Log.d("#E2E", "createKeysBackupVersion")
|
||||
val version = keysBackup.createKeysBackupVersion(megolmBackupCreationInfo)
|
||||
|
||||
// Backup must be enable now
|
||||
|
@ -151,6 +155,7 @@ class KeysBackupTest : InstrumentedTest {
|
|||
val versionResult = keysBackup.getVersion(version.version)
|
||||
val trust = keysBackup.getKeysBackupTrust(versionResult!!)
|
||||
|
||||
Log.d("#E2E", "Check backup signatures")
|
||||
assertEquals("Should have 2 signatures", 2, trust.signatures.size)
|
||||
|
||||
trust.signatures
|
||||
|
@ -432,9 +437,13 @@ class KeysBackupTest : InstrumentedTest {
|
|||
.keysBackupService()
|
||||
.getKeysBackupTrust(keysVersionResult)
|
||||
|
||||
// - It must be trusted and must have 2 signatures now
|
||||
// The backup should have a valid signature from that device now
|
||||
assertTrue(keysBackupVersionTrust.usable)
|
||||
assertEquals(2, keysBackupVersionTrust.signatures.size)
|
||||
val signature = keysBackupVersionTrust.signatures
|
||||
.filterIsInstance<KeysBackupVersionTrustSignature.DeviceSignature>()
|
||||
.firstOrNull { it.deviceId == testData.aliceSession2.cryptoService().getMyCryptoDevice().deviceId }
|
||||
assertNotNull(signature)
|
||||
assertTrue(signature!!.valid)
|
||||
|
||||
stateObserver.stopAndCheckStates(null)
|
||||
}
|
||||
|
@ -492,9 +501,16 @@ class KeysBackupTest : InstrumentedTest {
|
|||
.keysBackupService()
|
||||
.getKeysBackupTrust(keysVersionResult)
|
||||
|
||||
// - It must be trusted and must have 2 signatures now
|
||||
// // - It must be trusted and must have 2 signatures now
|
||||
// assertTrue(keysBackupVersionTrust.usable)
|
||||
// assertEquals(2, keysBackupVersionTrust.signatures.size)
|
||||
// The backup should have a valid signature from that device now
|
||||
assertTrue(keysBackupVersionTrust.usable)
|
||||
assertEquals(2, keysBackupVersionTrust.signatures.size)
|
||||
val signature = keysBackupVersionTrust.signatures
|
||||
.filterIsInstance<KeysBackupVersionTrustSignature.DeviceSignature>()
|
||||
.firstOrNull { it.deviceId == testData.aliceSession2.cryptoService().getMyCryptoDevice().deviceId }
|
||||
assertNotNull(signature)
|
||||
assertTrue(signature!!.valid)
|
||||
|
||||
stateObserver.stopAndCheckStates(null)
|
||||
}
|
||||
|
@ -590,9 +606,17 @@ class KeysBackupTest : InstrumentedTest {
|
|||
.keysBackupService()
|
||||
.getKeysBackupTrust(keysVersionResult)
|
||||
|
||||
// - It must be trusted and must have 2 signatures now
|
||||
// // - It must be trusted and must have 2 signatures now
|
||||
// assertTrue(keysBackupVersionTrust.usable)
|
||||
// assertEquals(2, keysBackupVersionTrust.signatures.size)
|
||||
|
||||
// - It must be trusted and signed by current device
|
||||
assertTrue(keysBackupVersionTrust.usable)
|
||||
assertEquals(2, keysBackupVersionTrust.signatures.size)
|
||||
val signature = keysBackupVersionTrust.signatures
|
||||
.filterIsInstance<KeysBackupVersionTrustSignature.DeviceSignature>()
|
||||
.firstOrNull { it.deviceId == testData.aliceSession2.cryptoService().getMyCryptoDevice().deviceId }
|
||||
assertNotNull(signature)
|
||||
assertTrue(signature!!.valid)
|
||||
|
||||
stateObserver.stopAndCheckStates(null)
|
||||
}
|
||||
|
@ -672,11 +696,16 @@ class KeysBackupTest : InstrumentedTest {
|
|||
*/
|
||||
@Test
|
||||
fun testBackupWithPassword() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
val password = "password"
|
||||
|
||||
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(password)
|
||||
Assume.assumeTrue(
|
||||
"Can't report progress same way in rust",
|
||||
testData.cryptoTestData.firstSession.cryptoService().name() != "rust-sdk"
|
||||
)
|
||||
|
||||
// - Restore the e2e backup with the password
|
||||
val steps = ArrayList<StepProgressListener.Step>()
|
||||
|
@ -887,6 +916,7 @@ class KeysBackupTest : InstrumentedTest {
|
|||
* -> It must success
|
||||
*/
|
||||
@Test
|
||||
@Ignore("Instable on both flavors")
|
||||
fun testBackupAfterVerifyingADevice() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||
|
||||
|
@ -930,9 +960,13 @@ class KeysBackupTest : InstrumentedTest {
|
|||
assertEquals("Backup state must be NotTrusted", KeysBackupState.NotTrusted, keysBackup2.getState())
|
||||
assertFalse("Backup should not be enabled", keysBackup2.isEnabled())
|
||||
|
||||
val signatures = keysBackup2.getCurrentVersion()?.toKeysVersionResult()?.getAuthDataAsMegolmBackupAuthData()?.signatures
|
||||
Log.d("#E2E", "keysBackup2 signatures: $signatures")
|
||||
|
||||
// - Validate the old device from the new one
|
||||
cryptoTestHelper.verifyNewSession(cryptoTestData.firstSession, aliceSession2)
|
||||
|
||||
cryptoTestData.firstSession.cryptoService().keysBackupService().checkAndStartKeysBackup()
|
||||
// -> Backup should automatically enable on the new device
|
||||
suspendCancellableCoroutine<Unit> { continuation ->
|
||||
val listener = object : KeysBackupStateListener {
|
||||
|
@ -954,11 +988,11 @@ class KeysBackupTest : InstrumentedTest {
|
|||
assertEquals(oldKeyBackupVersion, aliceSession2.cryptoService().keysBackupService().currentBackupVersion)
|
||||
|
||||
// aliceSession2.cryptoService().keysBackupService().backupAllGroupSessions(null, it)
|
||||
testHelper.retryPeriodically {
|
||||
testHelper.retryWithBackoff {
|
||||
keysBackup2.getTotalNumbersOfKeys() == keysBackup2.getTotalNumbersOfBackedUpKeys()
|
||||
}
|
||||
|
||||
testHelper.retryPeriodically {
|
||||
testHelper.retryWithBackoff {
|
||||
aliceSession2.cryptoService().keysBackupService().getState() == KeysBackupState.ReadyToBackUp
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.junit.After
|
|||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
@ -65,6 +66,16 @@ class ElementAndroidToElementRMigrationTest : InstrumentedTest {
|
|||
|
||||
@Test
|
||||
fun given_a_valid_crypto_store_realm_file_then_migration_should_be_successful() {
|
||||
testMigrate(false)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("We don't migrate group session for now, and it makes test suite unstable")
|
||||
fun given_a_valid_crypto_store_realm_file_no_lazy_then_migration_should_be_successful() {
|
||||
testMigrate(true)
|
||||
}
|
||||
|
||||
private fun testMigrate(migrateGroupSessions: Boolean) {
|
||||
val realmName = "crypto_store_migration_16.realm"
|
||||
val migration = RealmCryptoStoreMigration(object : Clock {
|
||||
override fun epochMillis() = 0L
|
||||
|
@ -86,7 +97,7 @@ class ElementAndroidToElementRMigrationTest : InstrumentedTest {
|
|||
val deviceId = metaData.deviceId!!
|
||||
val olmAccount = metaData.getOlmAccount()!!
|
||||
|
||||
val extractor = MigrateEAtoEROperation()
|
||||
val extractor = MigrateEAtoEROperation(migrateGroupSessions)
|
||||
|
||||
val targetFile = File(configurationFactory.root, "rust-sdk")
|
||||
|
||||
|
@ -101,14 +112,16 @@ class ElementAndroidToElementRMigrationTest : InstrumentedTest {
|
|||
assertTrue(crossSigningStatus.hasSelfSigning)
|
||||
assertTrue(crossSigningStatus.hasUserSigning)
|
||||
|
||||
val inboundGroupSessionEntities = realm!!.where<OlmInboundGroupSessionEntity>().findAll()
|
||||
assertEquals(inboundGroupSessionEntities.size, machine.roomKeyCounts().total.toInt())
|
||||
if (migrateGroupSessions) {
|
||||
val inboundGroupSessionEntities = realm!!.where<OlmInboundGroupSessionEntity>().findAll()
|
||||
assertEquals(inboundGroupSessionEntities.size, machine.roomKeyCounts().total.toInt())
|
||||
|
||||
val backedUpInboundGroupSessionEntities = realm!!
|
||||
.where<OlmInboundGroupSessionEntity>()
|
||||
.equalTo(OlmInboundGroupSessionEntityFields.BACKED_UP, true)
|
||||
.findAll()
|
||||
assertEquals(backedUpInboundGroupSessionEntities.size, machine.roomKeyCounts().backedUp.toInt())
|
||||
val backedUpInboundGroupSessionEntities = realm!!
|
||||
.where<OlmInboundGroupSessionEntity>()
|
||||
.equalTo(OlmInboundGroupSessionEntityFields.BACKED_UP, true)
|
||||
.findAll()
|
||||
assertEquals(backedUpInboundGroupSessionEntities.size, machine.roomKeyCounts().backedUp.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
// @Test
|
||||
|
|
|
@ -20,11 +20,11 @@ import io.realm.RealmConfiguration
|
|||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
||||
class MigrateEAtoEROperation {
|
||||
class MigrateEAtoEROperation(private val migrateGroupSessions: Boolean = false) {
|
||||
|
||||
fun execute(cryptoRealm: RealmConfiguration, sessionFilesDir: File, passphrase: String?): File {
|
||||
// to remove unused warning
|
||||
Timber.v("Not used in kotlin crypto $cryptoRealm ${"*".repeat(passphrase?.length ?: 0)}")
|
||||
Timber.v("Not used in kotlin crypto $cryptoRealm ${"*".repeat(passphrase?.length ?: 0)} lazy:$migrateGroupSessions")
|
||||
// no op in kotlinCrypto
|
||||
return sessionFilesDir
|
||||
}
|
||||
|
|
|
@ -82,6 +82,7 @@ import org.matrix.rustcomponents.sdk.crypto.RequestType
|
|||
import org.matrix.rustcomponents.sdk.crypto.RoomKeyCounts
|
||||
import org.matrix.rustcomponents.sdk.crypto.ShieldColor
|
||||
import org.matrix.rustcomponents.sdk.crypto.ShieldState
|
||||
import org.matrix.rustcomponents.sdk.crypto.SignatureVerification
|
||||
import org.matrix.rustcomponents.sdk.crypto.setLogger
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
@ -916,7 +917,7 @@ internal class OlmMachine @Inject constructor(
|
|||
}
|
||||
|
||||
@Throws(CryptoStoreException::class)
|
||||
suspend fun checkAuthDataSignature(authData: KeysAlgorithmAndData): Boolean {
|
||||
suspend fun checkAuthDataSignature(authData: KeysAlgorithmAndData): SignatureVerification {
|
||||
return withContext(coroutineDispatchers.computation) {
|
||||
val adapter = moshi
|
||||
.newBuilder()
|
||||
|
@ -929,7 +930,7 @@ internal class OlmMachine @Inject constructor(
|
|||
)
|
||||
)
|
||||
|
||||
inner.verifyBackup(serializedAuthData).trusted
|
||||
inner.verifyBackup(serializedAuthData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,10 +28,13 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.isVerified
|
|||
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
||||
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.internal.crypto.network.OutgoingRequestsProcessor
|
||||
import org.matrix.rustcomponents.sdk.crypto.Request
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class RustCrossSigningService @Inject constructor(
|
||||
private val olmMachine: OlmMachine,
|
||||
private val outgoingRequestsProcessor: OutgoingRequestsProcessor,
|
||||
private val computeShieldForGroup: ComputeShieldForGroupUseCase
|
||||
) : CrossSigningService {
|
||||
|
||||
|
@ -78,6 +81,10 @@ internal class RustCrossSigningService @Inject constructor(
|
|||
* Users needs to enter credentials
|
||||
*/
|
||||
override suspend fun initializeCrossSigning(uiaInterceptor: UserInteractiveAuthInterceptor?) {
|
||||
// ensure our keys are sent before initialising
|
||||
outgoingRequestsProcessor.processOutgoingRequests(olmMachine) {
|
||||
it is Request.KeysUpload
|
||||
}
|
||||
olmMachine.bootstrapCrossSigning(uiaInterceptor)
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ 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
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupVersionTrust
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupVersionTrustSignature
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysVersion
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysVersionResult
|
||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.MegolmBackupAuthData
|
||||
|
@ -67,6 +68,8 @@ import org.matrix.android.sdk.internal.util.JsonCanonicalizer
|
|||
import org.matrix.olm.OlmException
|
||||
import org.matrix.rustcomponents.sdk.crypto.Request
|
||||
import org.matrix.rustcomponents.sdk.crypto.RequestType
|
||||
import org.matrix.rustcomponents.sdk.crypto.SignatureState
|
||||
import org.matrix.rustcomponents.sdk.crypto.SignatureVerification
|
||||
import timber.log.Timber
|
||||
import java.security.InvalidParameterException
|
||||
import javax.inject.Inject
|
||||
|
@ -266,14 +269,56 @@ internal class RustKeyBackupService @Inject constructor(
|
|||
private suspend fun checkBackupTrust(algAndData: KeysAlgorithmAndData?): KeysBackupVersionTrust {
|
||||
if (algAndData == null) return KeysBackupVersionTrust(usable = false)
|
||||
try {
|
||||
val isTrusted = olmMachine.checkAuthDataSignature(algAndData)
|
||||
return KeysBackupVersionTrust(isTrusted)
|
||||
val authData = olmMachine.checkAuthDataSignature(algAndData)
|
||||
val signatures = authData.mapRustToAPI()
|
||||
return KeysBackupVersionTrust(authData.trusted, signatures)
|
||||
} catch (failure: Throwable) {
|
||||
Timber.w(failure, "Failed to trust backup")
|
||||
return KeysBackupVersionTrust(usable = false)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun SignatureVerification.mapRustToAPI(): List<KeysBackupVersionTrustSignature> {
|
||||
val signatures = mutableListOf<KeysBackupVersionTrustSignature>()
|
||||
// signature state of own device
|
||||
val ownDeviceState = this.deviceSignature
|
||||
if (ownDeviceState != SignatureState.MISSING && ownDeviceState != SignatureState.INVALID) {
|
||||
// we can add it
|
||||
signatures.add(
|
||||
KeysBackupVersionTrustSignature.DeviceSignature(
|
||||
olmMachine.deviceId(),
|
||||
olmMachine.getCryptoDeviceInfo(olmMachine.userId(), olmMachine.deviceId()),
|
||||
ownDeviceState == SignatureState.VALID_AND_TRUSTED
|
||||
)
|
||||
)
|
||||
}
|
||||
// signature state of our own identity
|
||||
val ownIdentityState = this.userIdentitySignature
|
||||
if (ownIdentityState != SignatureState.MISSING && ownIdentityState != SignatureState.INVALID) {
|
||||
// we can add it
|
||||
val masterKey = olmMachine.getIdentity(olmMachine.userId())?.toMxCrossSigningInfo()?.masterKey()
|
||||
signatures.add(
|
||||
KeysBackupVersionTrustSignature.UserSignature(
|
||||
masterKey?.unpaddedBase64PublicKey,
|
||||
masterKey,
|
||||
ownIdentityState == SignatureState.VALID_AND_TRUSTED
|
||||
)
|
||||
)
|
||||
}
|
||||
signatures.addAll(
|
||||
this.otherDevicesSignatures
|
||||
.filter { it.value == SignatureState.VALID_AND_TRUSTED || it.value == SignatureState.VALID_BUT_NOT_TRUSTED }
|
||||
.map {
|
||||
KeysBackupVersionTrustSignature.DeviceSignature(
|
||||
it.key,
|
||||
olmMachine.getCryptoDeviceInfo(olmMachine.userId(), it.key),
|
||||
ownDeviceState == SignatureState.VALID_AND_TRUSTED
|
||||
)
|
||||
}
|
||||
)
|
||||
return signatures
|
||||
}
|
||||
|
||||
override suspend fun getKeysBackupTrust(keysBackupVersion: KeysVersionResult): KeysBackupVersionTrust {
|
||||
return withContext(coroutineDispatchers.crypto) {
|
||||
checkBackupTrust(keysBackupVersion)
|
||||
|
@ -341,7 +386,7 @@ internal class RustKeyBackupService @Inject constructor(
|
|||
val authData = getMegolmBackupAuthData(keysBackupData)
|
||||
|
||||
when {
|
||||
authData == null -> {
|
||||
authData == null -> {
|
||||
Timber.w("isValidRecoveryKeyForKeysBackupVersion: Key backup is missing required data")
|
||||
throw IllegalArgumentException("Missing element")
|
||||
}
|
||||
|
@ -349,7 +394,7 @@ internal class RustKeyBackupService @Inject constructor(
|
|||
Timber.w("isValidRecoveryKeyForKeysBackupVersion: Public keys mismatch")
|
||||
throw IllegalArgumentException("Invalid recovery key or password")
|
||||
}
|
||||
else -> {
|
||||
else -> {
|
||||
// This case is fine, the public key on the server matches the public key the
|
||||
// recovery key produced.
|
||||
}
|
||||
|
@ -428,10 +473,10 @@ internal class RustKeyBackupService @Inject constructor(
|
|||
roomId != null && sessionId != null -> {
|
||||
sender.downloadBackedUpKeys(version, roomId, sessionId)
|
||||
}
|
||||
roomId != null -> {
|
||||
roomId != null -> {
|
||||
sender.downloadBackedUpKeys(version, roomId)
|
||||
}
|
||||
else -> {
|
||||
else -> {
|
||||
sender.downloadBackedUpKeys(version)
|
||||
}
|
||||
}
|
||||
|
@ -570,20 +615,24 @@ internal class RustKeyBackupService @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun restoreKeysWithRecoveryKey(keysVersionResult: KeysVersionResult,
|
||||
recoveryKey: IBackupRecoveryKey,
|
||||
roomId: String?,
|
||||
sessionId: String?,
|
||||
stepProgressListener: StepProgressListener?): ImportRoomKeysResult {
|
||||
override suspend fun restoreKeysWithRecoveryKey(
|
||||
keysVersionResult: KeysVersionResult,
|
||||
recoveryKey: IBackupRecoveryKey,
|
||||
roomId: String?,
|
||||
sessionId: String?,
|
||||
stepProgressListener: StepProgressListener?
|
||||
): ImportRoomKeysResult {
|
||||
Timber.v("restoreKeysWithRecoveryKey: From backup version: ${keysVersionResult.version}")
|
||||
return restoreBackup(keysVersionResult, recoveryKey, roomId, sessionId, stepProgressListener)
|
||||
}
|
||||
|
||||
override suspend fun restoreKeyBackupWithPassword(keysBackupVersion: KeysVersionResult,
|
||||
password: String,
|
||||
roomId: String?,
|
||||
sessionId: String?,
|
||||
stepProgressListener: StepProgressListener?): ImportRoomKeysResult {
|
||||
override suspend fun restoreKeyBackupWithPassword(
|
||||
keysBackupVersion: KeysVersionResult,
|
||||
password: String,
|
||||
roomId: String?,
|
||||
sessionId: String?,
|
||||
stepProgressListener: StepProgressListener?
|
||||
): ImportRoomKeysResult {
|
||||
Timber.v("[MXKeyBackup] restoreKeyBackup with password: From backup version: ${keysBackupVersion.version}")
|
||||
val recoveryKey = withContext(coroutineDispatchers.crypto) {
|
||||
recoveryKeyFromPassword(password, keysBackupVersion)
|
||||
|
@ -752,13 +801,13 @@ internal class RustKeyBackupService @Inject constructor(
|
|||
val authData = getMegolmBackupAuthData(keysBackupData)
|
||||
|
||||
return when {
|
||||
authData == null -> {
|
||||
authData == null -> {
|
||||
throw IllegalArgumentException("recoveryKeyFromPassword: invalid parameter")
|
||||
}
|
||||
authData.privateKeySalt.isNullOrBlank() || authData.privateKeyIterations == null -> {
|
||||
throw java.lang.IllegalArgumentException("recoveryKeyFromPassword: Salt and/or iterations not found in key backup auth data")
|
||||
}
|
||||
else -> {
|
||||
else -> {
|
||||
BackupRecoveryKey.fromPassphrase(password, authData.privateKeySalt, authData.privateKeyIterations)
|
||||
}
|
||||
}
|
||||
|
@ -811,7 +860,7 @@ internal class RustKeyBackupService @Inject constructor(
|
|||
suspend fun maybeBackupKeys() {
|
||||
withContext(coroutineDispatchers.crypto) {
|
||||
when {
|
||||
isStuck() -> {
|
||||
isStuck() -> {
|
||||
// If not already done, or in error case, check for a valid backup version on the homeserver.
|
||||
// If there is one, maybeBackupKeys will be called again.
|
||||
checkAndStartKeysBackup()
|
||||
|
@ -829,7 +878,7 @@ internal class RustKeyBackupService @Inject constructor(
|
|||
tryOrNull("AUTO backup failed") { backupKeys() }
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
else -> {
|
||||
Timber.v("maybeBackupKeys: Skip it because state: ${getState()}")
|
||||
}
|
||||
}
|
||||
|
@ -907,7 +956,7 @@ internal class RustKeyBackupService @Inject constructor(
|
|||
// is available on the homeserver
|
||||
checkAndStartKeysBackup()
|
||||
}
|
||||
else ->
|
||||
else ->
|
||||
// Come back to the ready state so that we will retry on the next received key
|
||||
keysBackupStateManager.state = KeysBackupState.ReadyToBackUp
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ import kotlin.system.measureTimeMillis
|
|||
|
||||
private val charset = Charset.forName("UTF-8")
|
||||
|
||||
internal class ExtractMigrationDataUseCase {
|
||||
internal class ExtractMigrationDataUseCase(val migrateGroupSessions: Boolean = false) {
|
||||
|
||||
fun extractData(realm: Realm, importPartial: ((MigrationData) -> Unit)) {
|
||||
return try {
|
||||
|
@ -143,35 +143,37 @@ internal class ExtractMigrationDataUseCase {
|
|||
Timber.i("Migration: rust import time $writeTime")
|
||||
}
|
||||
|
||||
// We don't migrate outbound session directly after migration
|
||||
// We don't migrate outbound session by default directly after migration
|
||||
// We are going to do it lazyly when decryption fails
|
||||
// var migratedInboundGroupSessionCount = 0
|
||||
// readTime = 0
|
||||
// writeTime = 0
|
||||
// measureTimeMillis {
|
||||
// realm.where<OlmInboundGroupSessionEntity>()
|
||||
// .findAll()
|
||||
// .chunked(chunkSize) { chunk ->
|
||||
// val export: List<PickledInboundGroupSession>
|
||||
// measureTimeMillis {
|
||||
// export = chunk.mapNotNull { it.toPickledInboundGroupSession(pickleKey) }
|
||||
// }.also {
|
||||
// readTime += it
|
||||
// }
|
||||
// migratedInboundGroupSessionCount+=export.size
|
||||
// measureTimeMillis {
|
||||
// importPartial(
|
||||
// baseExtract.copy(inboundGroupSessions = export)
|
||||
// )
|
||||
// }.also {
|
||||
// writeTime += it
|
||||
// }
|
||||
// }
|
||||
// }.also {
|
||||
// Timber.i("Migration: took $it ms to migrate $migratedInboundGroupSessionCount group sessions")
|
||||
// Timber.i("Migration: extract time $readTime")
|
||||
// Timber.i("Migration: rust import time $writeTime")
|
||||
// }
|
||||
if (migrateGroupSessions) {
|
||||
var migratedInboundGroupSessionCount = 0
|
||||
readTime = 0
|
||||
writeTime = 0
|
||||
measureTimeMillis {
|
||||
realm.where<OlmInboundGroupSessionEntity>()
|
||||
.findAll()
|
||||
.chunked(chunkSize) { chunk ->
|
||||
val export: List<PickledInboundGroupSession>
|
||||
measureTimeMillis {
|
||||
export = chunk.mapNotNull { it.toPickledInboundGroupSession(pickleKey) }
|
||||
}.also {
|
||||
readTime += it
|
||||
}
|
||||
migratedInboundGroupSessionCount += export.size
|
||||
measureTimeMillis {
|
||||
importPartial(
|
||||
baseExtract.copy(inboundGroupSessions = export)
|
||||
)
|
||||
}.also {
|
||||
writeTime += it
|
||||
}
|
||||
}
|
||||
}.also {
|
||||
Timber.i("Migration: took $it ms to migrate $migratedInboundGroupSessionCount group sessions")
|
||||
Timber.i("Migration: extract time $readTime")
|
||||
Timber.i("Migration: rust import time $writeTime")
|
||||
}
|
||||
}
|
||||
|
||||
// return baseExtract
|
||||
}
|
||||
|
|
|
@ -23,14 +23,14 @@ import org.matrix.rustcomponents.sdk.crypto.ProgressListener
|
|||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
||||
class MigrateEAtoEROperation {
|
||||
class MigrateEAtoEROperation(private val migrateGroupSessions: Boolean = false) {
|
||||
|
||||
fun execute(cryptoRealm: RealmConfiguration, rustFilesDir: File, passphrase: String?): File {
|
||||
// Temporary code for migration
|
||||
if (!rustFilesDir.exists()) {
|
||||
rustFilesDir.mkdir()
|
||||
// perform a migration?
|
||||
val extractMigrationData = ExtractMigrationDataUseCase()
|
||||
val extractMigrationData = ExtractMigrationDataUseCase(migrateGroupSessions)
|
||||
val hasExitingData = extractMigrationData.hasExistingData(cryptoRealm)
|
||||
if (!hasExitingData) return rustFilesDir
|
||||
|
||||
|
|
Loading…
Reference in a new issue