Gossip keybackup key after verification!
This commit is contained in:
parent
0164f94047
commit
5b4b5e7a57
@ -21,3 +21,5 @@ const val MASTER_KEY_SSSS_NAME = "m.cross_signing.master"
|
||||
const val USER_SIGNING_KEY_SSSS_NAME = "m.cross_signing.user_signing"
|
||||
|
||||
const val SELF_SIGNING_KEY_SSSS_NAME = "m.cross_signing.self_signing"
|
||||
|
||||
const val KEYBACKUP_SECRET_SSSS_NAME = "m.megolm_backup.v1"
|
||||
|
@ -24,6 +24,7 @@ import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCre
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
|
||||
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
|
||||
import im.vector.matrix.android.internal.crypto.store.SavedKeyBackupKeyInfo
|
||||
|
||||
interface KeysBackupService {
|
||||
/**
|
||||
@ -172,6 +173,9 @@ interface KeysBackupService {
|
||||
password: String,
|
||||
callback: MatrixCallback<Unit>)
|
||||
|
||||
|
||||
fun onSecretKeyGossip(recoveryKey: String)
|
||||
|
||||
/**
|
||||
* Restore a backup with a recovery key from a given backup version stored on the homeserver.
|
||||
*
|
||||
@ -210,4 +214,9 @@ interface KeysBackupService {
|
||||
val isEnabled: Boolean
|
||||
val isStucked: Boolean
|
||||
val state: KeysBackupState
|
||||
|
||||
|
||||
// For gossiping
|
||||
fun saveBackupRecoveryKey(recoveryKey: String?, version: String?)
|
||||
fun getKeyBackupRecoveryKeyInfo() : SavedKeyBackupKeyInfo?
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ import im.vector.matrix.android.api.failure.Failure
|
||||
import im.vector.matrix.android.api.listeners.ProgressListener
|
||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||
import im.vector.matrix.android.api.session.crypto.MXCryptoError
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
|
||||
import im.vector.matrix.android.api.session.crypto.keyshare.GossipingRequestListener
|
||||
@ -775,6 +776,10 @@ internal class DefaultCryptoService @Inject constructor(
|
||||
crossSigningService.onSecretUSKGossip(secretContent.secretValue)
|
||||
return
|
||||
}
|
||||
KEYBACKUP_SECRET_SSSS_NAME -> {
|
||||
keysBackupService.onSecretKeyGossip(secretContent.secretValue)
|
||||
return
|
||||
}
|
||||
else -> {
|
||||
// Ask to application layer?
|
||||
Timber.v("## onSecretSend() : secret not handled by SDK")
|
||||
|
@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.crypto
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.api.auth.data.sessionId
|
||||
import im.vector.matrix.android.api.crypto.MXCryptoConfig
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
|
||||
import im.vector.matrix.android.api.session.crypto.keyshare.GossipingRequestListener
|
||||
@ -281,6 +282,7 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
||||
when (secretName) {
|
||||
SELF_SIGNING_KEY_SSSS_NAME -> cryptoStore.getCrossSigningPrivateKeys()?.selfSigned
|
||||
USER_SIGNING_KEY_SSSS_NAME -> cryptoStore.getCrossSigningPrivateKeys()?.user
|
||||
KEYBACKUP_SECRET_SSSS_NAME -> cryptoStore.getKeyBackupRecoveryKeyInfo()?.recoveryKey
|
||||
else -> null
|
||||
}?.let { secretValue ->
|
||||
Timber.i("## GOSSIP processIncomingSecretShareRequest() : Sharing secret $secretName with $device locally trusted")
|
||||
|
@ -67,6 +67,7 @@ import im.vector.matrix.android.internal.crypto.keysbackup.util.extractCurveKeyF
|
||||
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
|
||||
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper
|
||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||
import im.vector.matrix.android.internal.crypto.store.SavedKeyBackupKeyInfo
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.KeysBackupDataEntity
|
||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||
import im.vector.matrix.android.internal.di.UserId
|
||||
@ -580,6 +581,28 @@ internal class DefaultKeysBackupService @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSecretKeyGossip(recoveryKey: String) {
|
||||
|
||||
Timber.v("onSecretKeyGossip: version ${keysBackupVersion?.version}")
|
||||
|
||||
cryptoCoroutineScope.launch(coroutineDispatchers.main) {
|
||||
try {
|
||||
val keysBackupVersion = getKeysBackupLastVersionTask.execute(Unit)
|
||||
if (isValidRecoveryKeyForKeysBackupVersion(recoveryKey, keysBackupVersion)) {
|
||||
awaitCallback<Unit> {
|
||||
trustKeysBackupVersion(keysBackupVersion, true, it)
|
||||
}
|
||||
val importResult = awaitCallback<ImportRoomKeysResult> {
|
||||
restoreKeysWithRecoveryKey(keysBackupVersion, recoveryKey, null, null, null, it)
|
||||
}
|
||||
Timber.i("onSecretKeyGossip: Recovered keys ${importResult.successfullyNumberOfImportedKeys} out of ${importResult.totalNumberOfKeys}")
|
||||
}
|
||||
} catch (failure: Throwable) {
|
||||
Timber.e("onSecretKeyGossip: failed to trust key backup version ${keysBackupVersion?.version}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get public key from a Recovery key
|
||||
*
|
||||
@ -1391,6 +1414,14 @@ internal class DefaultKeysBackupService @Inject constructor(
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
override fun getKeyBackupRecoveryKeyInfo(): SavedKeyBackupKeyInfo? {
|
||||
return cryptoStore.getKeyBackupRecoveryKeyInfo()
|
||||
}
|
||||
|
||||
override fun saveBackupRecoveryKey(recoveryKey: String?, version: String?) {
|
||||
cryptoStore.saveBackupRecoveryKey(recoveryKey, version)
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Maximum delay in ms in {@link maybeBackupKeys}
|
||||
private const val KEY_BACKUP_WAITING_TIME_TO_SEND_KEY_BACKUP_MILLIS = 10_000L
|
||||
|
@ -405,6 +405,9 @@ internal interface IMXCryptoStore {
|
||||
|
||||
fun getCrossSigningPrivateKeys(): PrivateKeysInfo?
|
||||
|
||||
fun saveBackupRecoveryKey(recoveryKey: String?, version: String?)
|
||||
fun getKeyBackupRecoveryKeyInfo() : SavedKeyBackupKeyInfo?
|
||||
|
||||
fun setUserKeysAsTrusted(userId: String, trusted: Boolean = true)
|
||||
fun setDeviceTrust(userId: String, deviceId: String, crossSignedVerified: Boolean, locallyVerified: Boolean)
|
||||
|
||||
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2020 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 im.vector.matrix.android.internal.crypto.store
|
||||
|
||||
data class SavedKeyBackupKeyInfo (
|
||||
val recoveryKey : String,
|
||||
val version: String
|
||||
)
|
@ -45,6 +45,7 @@ import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
|
||||
import im.vector.matrix.android.internal.crypto.model.toEntity
|
||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||
import im.vector.matrix.android.internal.crypto.store.PrivateKeysInfo
|
||||
import im.vector.matrix.android.internal.crypto.store.SavedKeyBackupKeyInfo
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.CrossSigningInfoEntity
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.CrossSigningInfoEntityFields
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.CryptoMapper
|
||||
@ -389,6 +390,29 @@ internal class RealmCryptoStore @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override fun saveBackupRecoveryKey(recoveryKey: String?, version: String?) {
|
||||
doRealmTransaction(realmConfiguration) { realm ->
|
||||
realm.where<CryptoMetadataEntity>().findFirst()?.apply {
|
||||
keyBackupRecoveryKey = recoveryKey
|
||||
keyBackupRecoveryKeyVersion = version
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getKeyBackupRecoveryKeyInfo(): SavedKeyBackupKeyInfo? {
|
||||
return doRealmQueryAndCopy(realmConfiguration) { realm ->
|
||||
realm.where<CryptoMetadataEntity>().findFirst()
|
||||
}?.let {
|
||||
val key = it.keyBackupRecoveryKey
|
||||
val version = it.keyBackupRecoveryKeyVersion
|
||||
if (!key.isNullOrBlank() && !version.isNullOrBlank()) {
|
||||
SavedKeyBackupKeyInfo(recoveryKey = key, version = version)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun storeSSKPrivateKey(ssk: String?) {
|
||||
doRealmTransaction(realmConfiguration) { realm ->
|
||||
realm.where<CryptoMetadataEntity>().findFirst()?.apply {
|
||||
|
@ -37,13 +37,14 @@ import timber.log.Timber
|
||||
internal object RealmCryptoStoreMigration : RealmMigration {
|
||||
|
||||
// Version 1L added Cross Signing info persistence
|
||||
const val CRYPTO_STORE_SCHEMA_VERSION = 2L
|
||||
const val CRYPTO_STORE_SCHEMA_VERSION = 3L
|
||||
|
||||
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
|
||||
Timber.v("Migrating Realm Crypto from $oldVersion to $newVersion")
|
||||
|
||||
if (oldVersion <= 0) migrateTo1(realm)
|
||||
if (oldVersion <= 1) migrateTo2(realm)
|
||||
if (oldVersion <= 2) migrateTo3(realm)
|
||||
}
|
||||
|
||||
private fun migrateTo1(realm: DynamicRealm) {
|
||||
@ -185,4 +186,12 @@ internal object RealmCryptoStoreMigration : RealmMigration {
|
||||
.addIndex(OutgoingGossipingRequestEntityFields.TYPE_STR)
|
||||
.addField(OutgoingGossipingRequestEntityFields.REQUEST_STATE_STR, String::class.java)
|
||||
}
|
||||
|
||||
|
||||
private fun migrateTo3(realm: DynamicRealm) {
|
||||
Timber.d("Updating CryptoMetadataEntity table")
|
||||
realm.schema.get("CryptoMetadataEntity")
|
||||
?.addField(CryptoMetadataEntityFields.KEY_BACKUP_RECOVERY_KEY, String::class.java)
|
||||
?.addField(CryptoMetadataEntityFields.KEY_BACKUP_RECOVERY_KEY_VERSION, String::class.java)
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,9 @@ internal open class CryptoMetadataEntity(
|
||||
|
||||
var xSignMasterPrivateKey: String? = null,
|
||||
var xSignUserPrivateKey: String? = null,
|
||||
var xSignSelfSignedPrivateKey: String? = null
|
||||
var xSignSelfSignedPrivateKey: String? = null,
|
||||
var keyBackupRecoveryKey: String? = null,
|
||||
var keyBackupRecoveryKeyVersion: String? = null
|
||||
|
||||
// var crossSigningInfoEntity: CrossSigningInfoEntity? = null
|
||||
) : RealmObject() {
|
||||
|
@ -17,6 +17,7 @@ package im.vector.matrix.android.internal.crypto.verification
|
||||
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.CrossSigningService
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
|
||||
import im.vector.matrix.android.api.session.crypto.verification.VerificationTransaction
|
||||
@ -103,6 +104,7 @@ internal abstract class DefaultVerificationTransaction(
|
||||
if (otherUserId == userId && !crossSigningService.canCrossSign()) {
|
||||
outgoingGossipingRequestManager.sendSecretShareRequest(SELF_SIGNING_KEY_SSSS_NAME, mapOf(userId to listOf(otherDeviceId ?: "*")))
|
||||
outgoingGossipingRequestManager.sendSecretShareRequest(USER_SIGNING_KEY_SSSS_NAME, mapOf(userId to listOf(otherDeviceId ?: "*")))
|
||||
outgoingGossipingRequestManager.sendSecretShareRequest(KEYBACKUP_SECRET_SSSS_NAME, mapOf(userId to listOf(otherDeviceId ?: "*")))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,8 @@ import im.vector.matrix.android.api.session.securestorage.SharedSecretStorageSer
|
||||
import im.vector.matrix.android.api.session.securestorage.SsssKeyCreationInfo
|
||||
import im.vector.matrix.android.internal.auth.data.LoginFlowTypes
|
||||
import im.vector.matrix.android.internal.auth.registration.RegistrationFlowResponse
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.MegolmBackupCreationInfo
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersion
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
|
||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||
import im.vector.matrix.android.internal.util.awaitCallback
|
||||
@ -36,6 +38,8 @@ import im.vector.riotx.core.resources.StringProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import java.util.Timer
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -195,7 +199,21 @@ class BootstrapCrossSigningTask @Inject constructor(
|
||||
return BootstrapResult.FailedToStorePrivateKeyInSSSS(failure)
|
||||
}
|
||||
|
||||
// TODO configure key backup?
|
||||
params.progressListener?.onProgress(WaitingViewData(stringProvider.getString(R.string.bootstrap_crosssigning_progress_key_backup), isIndeterminate = true))
|
||||
try {
|
||||
val creationInfo = awaitCallback<MegolmBackupCreationInfo> {
|
||||
session.cryptoService().keysBackupService().prepareKeysBackupVersion(null, null, it)
|
||||
}
|
||||
val version = awaitCallback<KeysVersion> {
|
||||
session.cryptoService().keysBackupService().createKeysBackupVersion(creationInfo, it)
|
||||
}
|
||||
// Save it for gossiping
|
||||
session.cryptoService().keysBackupService().saveBackupRecoveryKey(creationInfo.recoveryKey, version = version.version)
|
||||
|
||||
} catch (failure: Throwable) {
|
||||
Timber.e("## BootstrapCrossSigningTask: Failed to init keybackup")
|
||||
}
|
||||
|
||||
|
||||
return BootstrapResult.Success(keyInfo)
|
||||
}
|
||||
|
@ -71,6 +71,7 @@
|
||||
<string name="bootstrap_crosssigning_progress_save_msk">Synchronizing Master key</string>
|
||||
<string name="bootstrap_crosssigning_progress_save_usk">Synchronizing User key</string>
|
||||
<string name="bootstrap_crosssigning_progress_save_ssk">Synchronizing Self Signing key</string>
|
||||
<string name="bootstrap_crosssigning_progress_key_backup">Setting Up Key Backup</string>
|
||||
|
||||
|
||||
<!-- %1$s is replaced by message_key and %2$s by recovery_passphrase -->
|
||||
|
Loading…
x
Reference in New Issue
Block a user