Create isCrossSigningInitialized(). Do not display the conclusion Fragment anymore

This commit is contained in:
Benoit Marty 2020-06-26 12:59:40 +02:00 committed by Valere
parent e9706a3b64
commit a66010a1d8
8 changed files with 156 additions and 126 deletions

View File

@ -43,9 +43,11 @@ interface CrossSigningService {
fun initializeCrossSigning(authParams: UserPasswordAuth?,
callback: MatrixCallback<Unit>)
fun isCrossSigningInitialized(): Boolean = getMyCrossSigningKeys() != null
fun checkTrustFromPrivateKeys(masterKeyPrivateKey: String?,
uskKeyPrivateKey: String?,
sskPrivateKey: String?) : UserTrustResult
sskPrivateKey: String?): UserTrustResult
fun getUserCrossSigningKeys(otherUserId: String): MXCrossSigningInfo?

View File

@ -150,6 +150,7 @@ internal class DefaultCrossSigningService @Inject constructor(
this.callbackThread = TaskThread.CRYPTO
this.callback = object : MatrixCallback<InitializeCrossSigningTask.Result> {
override fun onFailure(failure: Throwable) {
Timber.e(failure, "Error in initializeCrossSigning()")
callback.onFailure(failure)
}

View File

@ -16,8 +16,10 @@
package im.vector.riotx.features.crypto.recover
import im.vector.matrix.android.api.auth.data.LoginFlowTypes
import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.failure.MatrixError
import im.vector.matrix.android.api.failure.toRegistrationFlowResponse
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
@ -79,23 +81,23 @@ class BootstrapCrossSigningTask @Inject constructor(
override suspend fun execute(params: Params): BootstrapResult {
val crossSigningService = session.cryptoService().crossSigningService()
// TODO Remove
/*
params.progressListener?.onProgress(
WaitingViewData(
stringProvider.getString(R.string.bootstrap_crosssigning_progress_initializing),
isIndeterminate = true
)
)
// Ensure cross-signing is initialized. Due to migration it is maybe not always correctly initialized
if (!crossSigningService.isCrossSigningInitialized()) {
params.progressListener?.onProgress(
WaitingViewData(
stringProvider.getString(R.string.bootstrap_crosssigning_progress_initializing),
isIndeterminate = true
)
)
try {
awaitCallback<Unit> {
crossSigningService.initializeCrossSigning(params.userPasswordAuth, it)
try {
awaitCallback<Unit> {
crossSigningService.initializeCrossSigning(params.userPasswordAuth, it)
}
} catch (failure: Throwable) {
return handleInitializeXSigningError(failure)
}
} catch (failure: Throwable) {
return handleInitializeXSigningError(failure)
}
*/
val keyInfo: SsssKeyCreationInfo
@ -229,8 +231,6 @@ class BootstrapCrossSigningTask @Inject constructor(
return BootstrapResult.Success(keyInfo)
}
/*
TODO Remove
private fun handleInitializeXSigningError(failure: Throwable): BootstrapResult {
if (failure is Failure.ServerError && failure.error.code == MatrixError.M_FORBIDDEN) {
return BootstrapResult.InvalidPasswordError(failure.error)
@ -247,5 +247,4 @@ class BootstrapCrossSigningTask @Inject constructor(
}
return BootstrapResult.GenericError(failure)
}
*/
}

View File

@ -48,7 +48,12 @@ class BootstrapSaveRecoveryKeyFragment @Inject constructor(
recoverySave.clickableView.debouncedClicks { downloadRecoveryKey() }
recoveryCopy.clickableView.debouncedClicks { shareRecoveryKey() }
recoveryContinue.clickableView.debouncedClicks { sharedViewModel.handle(BootstrapActions.GoToCompleted) }
recoveryContinue.clickableView.debouncedClicks {
// We do not display the final Fragment anymore
// TODO Do some cleanup
// sharedViewModel.handle(BootstrapActions.GoToCompleted)
sharedViewModel.handle(BootstrapActions.Completed)
}
}
private fun downloadRecoveryKey() = withState(sharedViewModel) { _ ->

View File

@ -44,7 +44,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.OutputStream
class BootstrapSharedViewModel @AssistedInject constructor(
@Assisted initialState: BootstrapViewState,
@Assisted val args: BootstrapBottomSheet.Args,
@ -55,6 +54,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
private val reAuthHelper: ReAuthHelper
) : VectorViewModel<BootstrapViewState, BootstrapActions, BootstrapViewEvents>(initialState) {
private var isBackupCreatedFromPassphrase: Boolean = false
private val zxcvbn = Zxcvbn()
@AssistedInject.Factory
@ -64,48 +64,48 @@ class BootstrapSharedViewModel @AssistedInject constructor(
private var _pendingSession: String? = null
private fun startProcess() {
init {
// need to check if user have an existing keybackup
if (args.isNewAccount) {
setState {
copy(step = BootstrapStep.CheckingMigration)
}
// We need to check if there is an existing backup
viewModelScope.launch(Dispatchers.IO) {
val version = awaitCallback<KeysVersionResult?> {
session.cryptoService().keysBackupService().getCurrentVersion(it)
}
if (version == null) {
// we just resume plain bootstrap
setState {
copy(step = BootstrapStep.FirstForm(keyBackUpExist = false))
}
} else {
// we need to get existing backup passphrase/key and convert to SSSS
val keyVersion = awaitCallback<KeysVersionResult?> {
session.cryptoService().keysBackupService().getVersion(version.version ?: "", it)
}
if (keyVersion == null) {
// strange case... just finish?
_viewEvents.post(BootstrapViewEvents.Dismiss)
} else {
isBackupCreatedFromPassphrase = keyVersion.getAuthDataAsMegolmBackupAuthData()?.privateKeySalt != null
setState {
copy(step = BootstrapStep.FirstForm(keyBackUpExist = true))
}
}
}
}
}
private fun handleStartMigratingKeyBackup() {
if (isBackupCreatedFromPassphrase) {
setState {
copy(step = BootstrapStep.SetupPassphrase(false))
copy(step = BootstrapStep.GetBackupSecretPassForMigration(isPasswordVisible = false, useKey = false))
}
} else {
setState {
copy(step = BootstrapStep.CheckingMigration)
}
// We need to check if there is an existing backup
viewModelScope.launch(Dispatchers.IO) {
val version = awaitCallback<KeysVersionResult?> {
session.cryptoService().keysBackupService().getCurrentVersion(it)
}
if (version == null) {
// we just resume plain bootstrap
setState {
copy(step = BootstrapStep.SetupPassphrase(false))
}
} else {
// we need to get existing backup passphrase/key and convert to SSSS
val keyVersion = awaitCallback<KeysVersionResult?> {
session.cryptoService().keysBackupService().getVersion(version.version ?: "", it)
}
if (keyVersion == null) {
// strange case... just finish?
_viewEvents.post(BootstrapViewEvents.Dismiss)
} else {
val isBackupCreatedFromPassphrase = keyVersion.getAuthDataAsMegolmBackupAuthData()?.privateKeySalt != null
if (isBackupCreatedFromPassphrase) {
setState {
copy(step = BootstrapStep.GetBackupSecretPassForMigration(isPasswordVisible = false, useKey = false))
}
} else {
setState {
copy(step = BootstrapStep.GetBackupSecretKeyForMigration)
}
}
}
}
copy(step = BootstrapStep.GetBackupSecretKeyForMigration)
}
}
}
@ -135,12 +135,14 @@ class BootstrapSharedViewModel @AssistedInject constructor(
copy(step = state.step.copy(isPasswordVisible = !state.step.isPasswordVisible))
}
}
else -> {
}
else -> Unit
}
}
BootstrapActions.SetupRecoveryKey -> {
startProcess()
BootstrapActions.StartKeyBackupMigration -> {
handleStartMigratingKeyBackup()
}
is BootstrapActions.Start -> {
handleStart(action)
}
is BootstrapActions.UpdateCandidatePassphrase -> {
val strength = zxcvbn.measure(action.pass)
@ -259,6 +261,18 @@ class BootstrapSharedViewModel @AssistedInject constructor(
}.exhaustive
}
private fun handleStart(action: BootstrapActions.Start) = withState {
if (action.userWantsToEnterPassphrase) {
setState {
copy(
step = BootstrapStep.SetupPassphrase(isPasswordVisible = false)
)
}
} else {
startInitializeFlow(null)
}
}
// =======================================
// Business Logic
// =======================================
@ -335,7 +349,9 @@ class BootstrapSharedViewModel @AssistedInject constructor(
}
}
private fun startInitializeFlow(userPassword: String?) {
private fun startInitializeFlow(userPassword: String?) = withState { state ->
val previousStep = state.step
setState {
copy(step = BootstrapStep.Initializing)
}
@ -350,70 +366,71 @@ class BootstrapSharedViewModel @AssistedInject constructor(
}
}
withState { state ->
viewModelScope.launch(Dispatchers.IO) {
val userPasswordAuth = userPassword?.let {
UserPasswordAuth(
// Note that _pendingSession may or may not be null, this is OK, it will be managed by the task
session = _pendingSession,
user = session.myUserId,
password = it
)
}
bootstrapTask.invoke(this,
Params(
userPasswordAuth = userPasswordAuth,
progressListener = progressListener,
passphrase = state.passphrase,
keySpec = state.migrationRecoveryKey?.let { extractCurveKeyFromRecoveryKey(it)?.let { RawBytesKeySpec(it) } }
)
) { bootstrapResult ->
when (bootstrapResult) {
is BootstrapResult.Success -> {
viewModelScope.launch(Dispatchers.IO) {
val userPasswordAuth = userPassword?.let {
UserPasswordAuth(
// Note that _pendingSession may or may not be null, this is OK, it will be managed by the task
session = _pendingSession,
user = session.myUserId,
password = it
)
}
bootstrapTask.invoke(this,
Params(
userPasswordAuth = userPasswordAuth,
progressListener = progressListener,
passphrase = state.passphrase,
keySpec = state.migrationRecoveryKey?.let { extractCurveKeyFromRecoveryKey(it)?.let { RawBytesKeySpec(it) } }
)
) { bootstrapResult ->
when (bootstrapResult) {
is BootstrapResult.Success -> {
setState {
copy(
recoveryKeyCreationInfo = bootstrapResult.keyInfo,
step = BootstrapStep.SaveRecoveryKey(false)
)
}
}
is BootstrapResult.PasswordAuthFlowMissing -> {
// Ask the password to the user
_pendingSession = bootstrapResult.sessionId
setState {
copy(
step = BootstrapStep.AccountPassword(false)
)
}
}
is BootstrapResult.UnsupportedAuthFlow -> {
_viewEvents.post(BootstrapViewEvents.ModalError(stringProvider.getString(R.string.auth_flow_not_supported)))
_viewEvents.post(BootstrapViewEvents.Dismiss)
}
is BootstrapResult.InvalidPasswordError -> {
// it's a bad password
// We clear the auth session, to avoid 'Requested operation has changed during the UI authentication session' error
_pendingSession = null
setState {
copy(
step = BootstrapStep.AccountPassword(false, stringProvider.getString(R.string.auth_invalid_login_param))
)
}
}
is BootstrapResult.Failure -> {
if (bootstrapResult is BootstrapResult.GenericError
&& bootstrapResult.failure is Failure.OtherServerError
&& bootstrapResult.failure.httpCode == 401) {
// Ignore this error
} else {
_viewEvents.post(BootstrapViewEvents.ModalError(bootstrapResult.error ?: stringProvider.getString(R.string.matrix_error)))
// Not sure
setState {
copy(
recoveryKeyCreationInfo = bootstrapResult.keyInfo,
step = BootstrapStep.SaveRecoveryKey(false)
step = previousStep
)
}
}
is BootstrapResult.PasswordAuthFlowMissing -> {
// Ask the password to the user
_pendingSession = bootstrapResult.sessionId
setState {
copy(
step = BootstrapStep.AccountPassword(false)
)
}
}
is BootstrapResult.UnsupportedAuthFlow -> {
_viewEvents.post(BootstrapViewEvents.ModalError(stringProvider.getString(R.string.auth_flow_not_supported)))
_viewEvents.post(BootstrapViewEvents.Dismiss)
}
is BootstrapResult.InvalidPasswordError -> {
// it's a bad password
// We clear the auth session, to avoid 'Requested operation has changed during the UI authentication session' error
_pendingSession = null
setState {
copy(
step = BootstrapStep.AccountPassword(false, stringProvider.getString(R.string.auth_invalid_login_param))
)
}
}
is BootstrapResult.Failure -> {
if (bootstrapResult is BootstrapResult.GenericError
&& bootstrapResult.failure is Failure.OtherServerError
&& bootstrapResult.failure.httpCode == 401) {
} else {
_viewEvents.post(BootstrapViewEvents.ModalError(bootstrapResult.error ?: stringProvider.getString(R.string.matrix_error)))
setState {
copy(
step = BootstrapStep.ConfirmPassphrase(false)
)
}
}
}
}
}
}
@ -469,7 +486,14 @@ class BootstrapSharedViewModel @AssistedInject constructor(
BootstrapStep.DoneSuccess -> {
// nop
}
}
BootstrapStep.CheckingMigration -> Unit
is BootstrapStep.FirstForm -> {
_viewEvents.post(BootstrapViewEvents.SkipBootstrap())
}
is BootstrapStep.GetBackupSecretForMigration -> {
_viewEvents.post(BootstrapViewEvents.SkipBootstrap())
}
}.exhaustive
}
// ======================================
@ -481,7 +505,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
override fun create(viewModelContext: ViewModelContext, state: BootstrapViewState): BootstrapSharedViewModel? {
val fragment: BootstrapBottomSheet = (viewModelContext as FragmentViewModelContext).fragment()
val args: BootstrapBottomSheet.Args = fragment.arguments?.getParcelable(BootstrapBottomSheet.EXTRA_ARGS)
?: BootstrapBottomSheet.Args(isNewAccount = true, initCrossSigningOnly = true)
?: BootstrapBottomSheet.Args(initCrossSigningOnly = true)
return fragment.bootstrapViewModelFactory.create(state, args)
}
}

View File

@ -119,8 +119,7 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor(
}
private fun refreshXSigningStatus() {
val crossSigningKeys = session.cryptoService().crossSigningService().getMyCrossSigningKeys()
val xSigningIsEnableInAccount = crossSigningKeys != null
val xSigningIsEnableInAccount = session.cryptoService().crossSigningService().isCrossSigningInitialized()
val xSigningKeysAreTrusted = session.cryptoService().crossSigningService().checkUserTrust(session.myUserId).isVerified()
val xSigningKeyCanSign = session.cryptoService().crossSigningService().canCrossSign()

View File

@ -54,7 +54,7 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As
setState {
copy(
hasAccountCrossSigning = session.cryptoService().crossSigningService().getMyCrossSigningKeys() != null,
hasAccountCrossSigning = session.cryptoService().crossSigningService().isCrossSigningInitialized(),
accountCrossSigningIsTrusted = session.cryptoService().crossSigningService().isCrossSigningVerified()
)
}

View File

@ -95,7 +95,7 @@ class DevicesViewModel @AssistedInject constructor(
setState {
copy(
hasAccountCrossSigning = session.cryptoService().crossSigningService().getMyCrossSigningKeys() != null,
hasAccountCrossSigning = session.cryptoService().crossSigningService().isCrossSigningInitialized(),
accountCrossSigningIsTrusted = session.cryptoService().crossSigningService().isCrossSigningVerified(),
myDeviceId = session.sessionParams.deviceId ?: ""
)