Fixes #1214
This commit is contained in:
parent
3968bb3488
commit
34dec64d9c
|
@ -14,6 +14,7 @@ Improvements 🙌:
|
||||||
- Cross-Signing | Setup key backup as part of SSSS bootstrapping (#1201)
|
- Cross-Signing | Setup key backup as part of SSSS bootstrapping (#1201)
|
||||||
- Cross-Signing | Gossip key backup recovery key (#1200)
|
- Cross-Signing | Gossip key backup recovery key (#1200)
|
||||||
- Show room encryption status as a bubble tile (#1078)
|
- Show room encryption status as a bubble tile (#1078)
|
||||||
|
- Cross-Signing | Restore history after recover from passphrase (#1214)
|
||||||
|
|
||||||
Bugfix 🐛:
|
Bugfix 🐛:
|
||||||
- Missing avatar/displayname after verification request message (#841)
|
- Missing avatar/displayname after verification request message (#841)
|
||||||
|
|
|
@ -37,6 +37,7 @@ import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import timber.log.Timber
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
|
|
||||||
data class SharedSecureStorageViewState(
|
data class SharedSecureStorageViewState(
|
||||||
|
@ -117,11 +118,15 @@ class SharedSecureStorageViewModel @AssistedInject constructor(
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
args.requestedSecrets.forEach {
|
args.requestedSecrets.forEach {
|
||||||
val res = awaitCallback<String> { callback ->
|
val res = awaitCallback<String> { callback ->
|
||||||
session.sharedSecretStorageService.getSecret(
|
if (session.getAccountDataEvent(it) != null) {
|
||||||
name = it,
|
session.sharedSecretStorageService.getSecret(
|
||||||
keyId = keyInfo.id,
|
name = it,
|
||||||
secretKey = keySpec,
|
keyId = keyInfo.id,
|
||||||
callback = callback)
|
secretKey = keySpec,
|
||||||
|
callback = callback)
|
||||||
|
} else {
|
||||||
|
Timber.w("## Cannot find secret $it in SSSS, skip")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
decryptedSecretMap[it] = res
|
decryptedSecretMap[it] = res
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ import com.airbnb.mvrx.MvRx
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
|
import im.vector.matrix.android.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
|
||||||
import im.vector.matrix.android.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
|
import im.vector.matrix.android.api.session.crypto.crosssigning.MASTER_KEY_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.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.crosssigning.USER_SIGNING_KEY_SSSS_NAME
|
||||||
|
@ -108,7 +109,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
startActivityForResult(SharedSecureStorageActivity.newIntent(
|
startActivityForResult(SharedSecureStorageActivity.newIntent(
|
||||||
requireContext(),
|
requireContext(),
|
||||||
null, // use default key
|
null, // use default key
|
||||||
listOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME),
|
listOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME, KEYBACKUP_SECRET_SSSS_NAME),
|
||||||
SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS
|
SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS
|
||||||
), SECRET_REQUEST_CODE)
|
), SECRET_REQUEST_CODE)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package im.vector.riotx.features.crypto.verification
|
package im.vector.riotx.features.crypto.verification
|
||||||
|
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.airbnb.mvrx.Async
|
import com.airbnb.mvrx.Async
|
||||||
import com.airbnb.mvrx.Fail
|
import com.airbnb.mvrx.Fail
|
||||||
import com.airbnb.mvrx.FragmentViewModelContext
|
import com.airbnb.mvrx.FragmentViewModelContext
|
||||||
|
@ -28,6 +29,7 @@ import com.squareup.inject.assisted.Assisted
|
||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
|
import im.vector.matrix.android.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
|
||||||
import im.vector.matrix.android.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
|
import im.vector.matrix.android.api.session.crypto.crosssigning.MASTER_KEY_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.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.crosssigning.USER_SIGNING_KEY_SSSS_NAME
|
||||||
|
@ -46,8 +48,13 @@ import im.vector.matrix.android.api.util.MatrixItem
|
||||||
import im.vector.matrix.android.api.util.toMatrixItem
|
import im.vector.matrix.android.api.util.toMatrixItem
|
||||||
import im.vector.matrix.android.internal.crypto.crosssigning.fromBase64
|
import im.vector.matrix.android.internal.crypto.crosssigning.fromBase64
|
||||||
import im.vector.matrix.android.internal.crypto.crosssigning.isVerified
|
import im.vector.matrix.android.internal.crypto.crosssigning.isVerified
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
|
||||||
|
import im.vector.matrix.android.internal.crypto.keysbackup.util.computeRecoveryKey
|
||||||
|
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
|
||||||
|
import im.vector.matrix.android.internal.util.awaitCallback
|
||||||
import im.vector.riotx.core.extensions.exhaustive
|
import im.vector.riotx.core.extensions.exhaustive
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
data class VerificationBottomSheetViewState(
|
data class VerificationBottomSheetViewState(
|
||||||
|
@ -334,40 +341,82 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
|
||||||
_viewEvents.post(VerificationBottomSheetViewEvents.AccessSecretStore)
|
_viewEvents.post(VerificationBottomSheetViewEvents.AccessSecretStore)
|
||||||
}
|
}
|
||||||
is VerificationAction.GotResultFromSsss -> {
|
is VerificationAction.GotResultFromSsss -> {
|
||||||
try {
|
handleSecretBackFromSSSS(action)
|
||||||
action.cypherData.fromBase64().inputStream().use { ins ->
|
|
||||||
val res = session.loadSecureSecret<Map<String, String>>(ins, action.alias)
|
|
||||||
val trustResult = session.cryptoService().crossSigningService().checkTrustFromPrivateKeys(
|
|
||||||
res?.get(MASTER_KEY_SSSS_NAME),
|
|
||||||
res?.get(USER_SIGNING_KEY_SSSS_NAME),
|
|
||||||
res?.get(SELF_SIGNING_KEY_SSSS_NAME)
|
|
||||||
)
|
|
||||||
if (trustResult.isVerified()) {
|
|
||||||
// Sign this device and upload the signature
|
|
||||||
session.sessionParams.credentials.deviceId?.let { deviceId ->
|
|
||||||
session.cryptoService()
|
|
||||||
.crossSigningService().trustDevice(deviceId, object : MatrixCallback<Unit> {
|
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
Timber.w(failure, "Failed to sign my device after recovery")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
setState {
|
|
||||||
copy(verifiedFromPrivateKeys = true)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// POP UP something
|
|
||||||
_viewEvents.post(VerificationBottomSheetViewEvents.ModalError("Failed to import keys"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
_viewEvents.post(VerificationBottomSheetViewEvents.ModalError(failure.localizedMessage))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleSecretBackFromSSSS(action: VerificationAction.GotResultFromSsss) {
|
||||||
|
try {
|
||||||
|
action.cypherData.fromBase64().inputStream().use { ins ->
|
||||||
|
val res = session.loadSecureSecret<Map<String, String>>(ins, action.alias)
|
||||||
|
val trustResult = session.cryptoService().crossSigningService().checkTrustFromPrivateKeys(
|
||||||
|
res?.get(MASTER_KEY_SSSS_NAME),
|
||||||
|
res?.get(USER_SIGNING_KEY_SSSS_NAME),
|
||||||
|
res?.get(SELF_SIGNING_KEY_SSSS_NAME)
|
||||||
|
)
|
||||||
|
if (trustResult.isVerified()) {
|
||||||
|
// Sign this device and upload the signature
|
||||||
|
session.sessionParams.credentials.deviceId?.let { deviceId ->
|
||||||
|
session.cryptoService()
|
||||||
|
.crossSigningService().trustDevice(deviceId, object : MatrixCallback<Unit> {
|
||||||
|
override fun onFailure(failure: Throwable) {
|
||||||
|
Timber.w(failure, "Failed to sign my device after recovery")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setState {
|
||||||
|
copy(verifiedFromPrivateKeys = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to get keybackup key
|
||||||
|
} else {
|
||||||
|
// POP UP something
|
||||||
|
_viewEvents.post(VerificationBottomSheetViewEvents.ModalError("Failed to import keys"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// try the keybackup
|
||||||
|
tentativeRestoreBackup(res)
|
||||||
|
Unit
|
||||||
|
}
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
_viewEvents.post(VerificationBottomSheetViewEvents.ModalError(failure.localizedMessage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun tentativeRestoreBackup(res: Map<String, String>?) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val secret = res?.get(KEYBACKUP_SECRET_SSSS_NAME) ?: return@launch Unit.also {
|
||||||
|
Timber.v("## Keybackup secret not restored from SSSS")
|
||||||
|
}
|
||||||
|
|
||||||
|
val version = awaitCallback<KeysVersionResult?> {
|
||||||
|
session.cryptoService().keysBackupService().getCurrentVersion(it)
|
||||||
|
} ?: return@launch
|
||||||
|
|
||||||
|
awaitCallback<ImportRoomKeysResult> {
|
||||||
|
session.cryptoService().keysBackupService().restoreKeysWithRecoveryKey(
|
||||||
|
version,
|
||||||
|
computeRecoveryKey(secret.fromBase64()),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
awaitCallback<Unit> {
|
||||||
|
session.cryptoService().keysBackupService().trustKeysBackupVersion(version, true, it)
|
||||||
|
}
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
// Just ignore for now
|
||||||
|
Timber.v("## Failed to restore backup after SSSS recovery")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun transactionCreated(tx: VerificationTransaction) {
|
override fun transactionCreated(tx: VerificationTransaction) {
|
||||||
transactionUpdated(tx)
|
transactionUpdated(tx)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue