mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-16 02:05:06 +08:00
Create isCrossSigningInitialized(). Do not display the conclusion Fragment anymore
This commit is contained in:
parent
e9706a3b64
commit
a66010a1d8
@ -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?
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
@ -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) { _ ->
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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()
|
||||
)
|
||||
}
|
||||
|
@ -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 ?: ""
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user