mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-21 17:29:32 +01:00
Start mirroring to SVR2.
This commit is contained in:
committed by
Clark Chen
parent
dfb7304626
commit
e1570e9512
@@ -29,7 +29,7 @@ import org.thoughtcrime.securesms.jobs.PreKeysSyncJob;
|
||||
import org.thoughtcrime.securesms.jobs.RotateCertificateJob;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationIds;
|
||||
import org.thoughtcrime.securesms.pin.PinState;
|
||||
import org.thoughtcrime.securesms.pin.SvrRepository;
|
||||
import org.thoughtcrime.securesms.push.AccountManagerFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
@@ -102,13 +102,8 @@ public final class RegistrationRepository {
|
||||
{
|
||||
return Single.<ServiceResponse<VerifyResponse>>fromCallable(() -> {
|
||||
try {
|
||||
String pin = response.getPin();
|
||||
registerAccountInternal(registrationData, response, setRegistrationLockEnabled);
|
||||
|
||||
if (pin != null && !pin.isEmpty()) {
|
||||
PinState.onPinChangedOrCreated(context, pin, SignalStore.pinValues().getKeyboardType());
|
||||
}
|
||||
|
||||
JobManager jobManager = ApplicationDependencies.getJobManager();
|
||||
jobManager.add(new DirectoryRefreshJob(false));
|
||||
jobManager.add(new RotateCertificateJob());
|
||||
@@ -176,7 +171,7 @@ public final class RegistrationRepository {
|
||||
TextSecurePreferences.setUnauthorizedReceived(context, false);
|
||||
NotificationManagerCompat.from(context).cancel(NotificationIds.UNREGISTERED_NOTIFICATION_ID);
|
||||
|
||||
PinState.onRegistration(context, response.getKbsData(), response.getPin(), hasPin, setRegistrationLockEnabled);
|
||||
SvrRepository.onRegistrationComplete(response.getMasterKey(), response.getPin(), hasPin, setRegistrationLockEnabled);
|
||||
|
||||
ApplicationDependencies.closeConnections();
|
||||
ApplicationDependencies.getIncomingMessageObserver();
|
||||
@@ -226,13 +221,13 @@ public final class RegistrationRepository {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Single<BackupAuthCheckProcessor> getKbsAuthCredential(@NonNull RegistrationData registrationData, List<String> usernamePasswords) {
|
||||
public Single<BackupAuthCheckProcessor> getSvrAuthCredential(@NonNull RegistrationData registrationData, List<String> usernamePasswords) {
|
||||
SignalServiceAccountManager accountManager = AccountManagerFactory.getInstance().createUnauthenticated(context, registrationData.getE164(), SignalServiceAddress.DEFAULT_DEVICE_ID, registrationData.getPassword());
|
||||
|
||||
return accountManager.checkBackupAuthCredentials(registrationData.getE164(), usernamePasswords)
|
||||
.map(BackupAuthCheckProcessor::new)
|
||||
.doOnSuccess(processor -> {
|
||||
if (SignalStore.kbsValues().removeAuthTokens(processor.getInvalid())) {
|
||||
if (SignalStore.svr().removeAuthTokens(processor.getInvalid())) {
|
||||
new BackupManager(context).dataChanged();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -22,7 +22,7 @@ public final class RegistrationUtil {
|
||||
if (!SignalStore.registrationValues().isRegistrationComplete() &&
|
||||
SignalStore.account().isRegistered() &&
|
||||
!Recipient.self().getProfileName().isEmpty() &&
|
||||
(SignalStore.kbsValues().hasPin() || SignalStore.kbsValues().hasOptedOut()))
|
||||
(SignalStore.svr().hasPin() || SignalStore.svr().hasOptedOut()))
|
||||
{
|
||||
Log.i(TAG, "Marking registration completed.", new Throwable());
|
||||
SignalStore.registrationValues().setRegistrationComplete();
|
||||
|
||||
@@ -10,15 +10,15 @@ import org.signal.libsignal.protocol.IdentityKeyPair
|
||||
import org.thoughtcrime.securesms.AppCapabilities
|
||||
import org.thoughtcrime.securesms.gcm.FcmUtil
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.pin.KeyBackupSystemWrongPinException
|
||||
import org.thoughtcrime.securesms.pin.SvrWrongPinException
|
||||
import org.thoughtcrime.securesms.push.AccountManagerFactory
|
||||
import org.thoughtcrime.securesms.registration.PushChallengeRequest.PushChallengeEvent
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.signalservice.api.KbsPinData
|
||||
import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager
|
||||
import org.whispersystems.signalservice.api.SvrNoDataException
|
||||
import org.whispersystems.signalservice.api.account.AccountAttributes
|
||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess
|
||||
import org.whispersystems.signalservice.api.kbs.MasterKey
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||
import org.whispersystems.signalservice.api.push.exceptions.NoSuchSessionException
|
||||
import org.whispersystems.signalservice.internal.ServiceResponse
|
||||
@@ -157,7 +157,7 @@ class VerifyAccountRepository(private val context: Application) {
|
||||
}.subscribeOn(Schedulers.io())
|
||||
}
|
||||
|
||||
fun registerAccount(sessionId: String?, registrationData: RegistrationData, pin: String? = null, kbsPinDataProducer: KbsPinDataProducer? = null): Single<ServiceResponse<VerifyResponse>> {
|
||||
fun registerAccount(sessionId: String?, registrationData: RegistrationData, pin: String? = null, masterKeyProducer: MasterKeyProducer? = null): Single<ServiceResponse<VerifyResponse>> {
|
||||
val universalUnidentifiedAccess: Boolean = TextSecurePreferences.isUniversalUnidentifiedAccess(context)
|
||||
val unidentifiedAccessKey: ByteArray = UnidentifiedAccess.deriveAccessKeyFrom(registrationData.profileKey)
|
||||
|
||||
@@ -168,15 +168,14 @@ class VerifyAccountRepository(private val context: Application) {
|
||||
registrationData.password
|
||||
)
|
||||
|
||||
val kbsData = kbsPinDataProducer?.produceKbsPinData()
|
||||
val registrationLockV2: String? = kbsData?.masterKey?.deriveRegistrationLock()
|
||||
val masterKey: MasterKey? = masterKeyProducer?.produceMasterKey()
|
||||
val registrationLock: String? = masterKey?.deriveRegistrationLock()
|
||||
|
||||
val accountAttributes = AccountAttributes(
|
||||
signalingKey = null,
|
||||
registrationId = registrationData.registrationId,
|
||||
fetchesMessages = registrationData.isNotFcm,
|
||||
pin = pin,
|
||||
registrationLock = registrationLockV2,
|
||||
registrationLock = registrationLock,
|
||||
unidentifiedAccessKey = unidentifiedAccessKey,
|
||||
unrestrictedUnidentifiedAccess = universalUnidentifiedAccess,
|
||||
capabilities = AppCapabilities.getCapabilities(true),
|
||||
@@ -197,7 +196,7 @@ class VerifyAccountRepository(private val context: Application) {
|
||||
|
||||
return Single.fromCallable {
|
||||
val response = accountManager.registerAccount(sessionId, registrationData.recoveryPassword, accountAttributes, aciPreKeyCollection, pniPreKeyCollection, registrationData.fcmToken, true)
|
||||
VerifyResponse.from(response, kbsData, pin, aciPreKeyCollection, pniPreKeyCollection)
|
||||
VerifyResponse.from(response, masterKey, pin, aciPreKeyCollection, pniPreKeyCollection)
|
||||
}.subscribeOn(Schedulers.io())
|
||||
}
|
||||
|
||||
@@ -207,9 +206,9 @@ class VerifyAccountRepository(private val context: Application) {
|
||||
}.subscribeOn(Schedulers.io())
|
||||
}
|
||||
|
||||
interface KbsPinDataProducer {
|
||||
@Throws(IOException::class, KeyBackupSystemWrongPinException::class, KeyBackupSystemNoDataException::class)
|
||||
fun produceKbsPinData(): KbsPinData
|
||||
interface MasterKeyProducer {
|
||||
@Throws(IOException::class, SvrWrongPinException::class, SvrNoDataException::class)
|
||||
fun produceMasterKey(): MasterKey
|
||||
}
|
||||
|
||||
enum class Mode(val isSmsRetrieverSupported: Boolean) {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package org.thoughtcrime.securesms.registration
|
||||
|
||||
import org.whispersystems.signalservice.api.KbsPinData
|
||||
import org.whispersystems.signalservice.api.account.PreKeyCollection
|
||||
import org.whispersystems.signalservice.api.kbs.MasterKey
|
||||
import org.whispersystems.signalservice.internal.ServiceResponse
|
||||
import org.whispersystems.signalservice.internal.push.VerifyAccountResponse
|
||||
|
||||
data class VerifyResponse(
|
||||
val verifyAccountResponse: VerifyAccountResponse,
|
||||
val kbsData: KbsPinData?,
|
||||
val masterKey: MasterKey?,
|
||||
val pin: String?,
|
||||
val aciPreKeyCollection: PreKeyCollection?,
|
||||
val pniPreKeyCollection: PreKeyCollection?
|
||||
@@ -15,13 +15,13 @@ data class VerifyResponse(
|
||||
companion object {
|
||||
fun from(
|
||||
response: ServiceResponse<VerifyAccountResponse>,
|
||||
kbsData: KbsPinData?,
|
||||
masterKey: MasterKey?,
|
||||
pin: String?,
|
||||
aciPreKeyCollection: PreKeyCollection?,
|
||||
pniPreKeyCollection: PreKeyCollection?
|
||||
): ServiceResponse<VerifyResponse> {
|
||||
return if (response.result.isPresent) {
|
||||
ServiceResponse.forResult(VerifyResponse(response.result.get(), kbsData, pin, aciPreKeyCollection, pniPreKeyCollection), 200, null)
|
||||
ServiceResponse.forResult(VerifyResponse(response.result.get(), masterKey, pin, aciPreKeyCollection, pniPreKeyCollection), 200, null)
|
||||
} else {
|
||||
ServiceResponse.coerceError(response)
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
package org.thoughtcrime.securesms.registration
|
||||
|
||||
import org.thoughtcrime.securesms.pin.KeyBackupSystemWrongPinException
|
||||
import org.thoughtcrime.securesms.pin.TokenData
|
||||
import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException
|
||||
import org.thoughtcrime.securesms.pin.SvrWrongPinException
|
||||
import org.thoughtcrime.securesms.registration.viewmodel.SvrAuthCredentialSet
|
||||
import org.whispersystems.signalservice.api.SvrNoDataException
|
||||
import org.whispersystems.signalservice.api.push.exceptions.IncorrectRegistrationRecoveryPasswordException
|
||||
import org.whispersystems.signalservice.api.push.exceptions.NoSuchSessionException
|
||||
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException
|
||||
import org.whispersystems.signalservice.internal.ServiceResponse
|
||||
import org.whispersystems.signalservice.internal.ServiceResponseProcessor
|
||||
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse
|
||||
import org.whispersystems.signalservice.internal.push.LockedException
|
||||
|
||||
/**
|
||||
@@ -16,7 +14,19 @@ import org.whispersystems.signalservice.internal.push.LockedException
|
||||
*/
|
||||
sealed class VerifyResponseProcessor(response: ServiceResponse<VerifyResponse>) : ServiceResponseProcessor<VerifyResponse>(response) {
|
||||
|
||||
open val tokenData: TokenData? = null
|
||||
open val svrTriesRemaining: Int?
|
||||
get() = (error as? SvrWrongPinException)?.triesRemaining
|
||||
|
||||
open val svrAuthCredentials: SvrAuthCredentialSet?
|
||||
get() {
|
||||
return error?.let {
|
||||
if (it is LockedException) {
|
||||
SvrAuthCredentialSet(it.svr1Credentials, it.svr2Credentials)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override fun authorizationFailed(): Boolean {
|
||||
return super.authorizationFailed()
|
||||
@@ -34,10 +44,6 @@ sealed class VerifyResponseProcessor(response: ServiceResponse<VerifyResponse>)
|
||||
return super.getError()
|
||||
}
|
||||
|
||||
fun invalidSession(): Boolean {
|
||||
return error is NoSuchSessionException
|
||||
}
|
||||
|
||||
fun getLockedException(): LockedException {
|
||||
return error as LockedException
|
||||
}
|
||||
@@ -50,34 +56,24 @@ sealed class VerifyResponseProcessor(response: ServiceResponse<VerifyResponse>)
|
||||
return error is IncorrectRegistrationRecoveryPasswordException
|
||||
}
|
||||
|
||||
abstract fun isKbsLocked(): Boolean
|
||||
/** True if the account has reglock enabled but all guesses have been exhausted, otherwise false. */
|
||||
abstract fun isRegistrationLockPresentAndSvrExhausted(): Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify processor specific to verifying without needing to handle registration lock.
|
||||
*/
|
||||
class VerifyResponseWithoutKbs(response: ServiceResponse<VerifyResponse>) : VerifyResponseProcessor(response) {
|
||||
override fun isKbsLocked(): Boolean {
|
||||
return registrationLock() && getLockedException().basicStorageCredentials == null
|
||||
override fun isRegistrationLockPresentAndSvrExhausted(): Boolean {
|
||||
return registrationLock() && getLockedException().svr1Credentials == null && getLockedException().svr2Credentials == null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify processor specific to verifying and successfully retrieving KBS information to
|
||||
* later attempt to verif with registration lock data (pin).
|
||||
* Verify processor indicating we cannot register until registration lock has been resolved.
|
||||
*/
|
||||
class VerifyResponseWithSuccessfulKbs(response: ServiceResponse<VerifyResponse>, override val tokenData: TokenData) : VerifyResponseProcessor(response) {
|
||||
override fun isKbsLocked(): Boolean {
|
||||
return registrationLock() && tokenData.triesRemaining == 0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify processor specific to verifying and unsuccessfully retrieving KBS information that
|
||||
* is required for attempting to verify a registration locked account.
|
||||
*/
|
||||
class VerifyResponseWithFailedKbs(response: ServiceResponse<TokenData>) : VerifyResponseProcessor(ServiceResponse.coerceError(response)) {
|
||||
override fun isKbsLocked(): Boolean {
|
||||
class VerifyResponseHitRegistrationLock(response: ServiceResponse<VerifyResponse>) : VerifyResponseProcessor(response) {
|
||||
override fun isRegistrationLockPresentAndSvrExhausted(): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -86,18 +82,14 @@ class VerifyResponseWithFailedKbs(response: ServiceResponse<TokenData>) : Verify
|
||||
* Process responses from attempting to verify an account with registration lock for use in
|
||||
* account registration.
|
||||
*/
|
||||
class VerifyResponseWithRegistrationLockProcessor(response: ServiceResponse<VerifyResponse>, override val tokenData: TokenData?) : VerifyResponseProcessor(response) {
|
||||
class VerifyResponseWithRegistrationLockProcessor(response: ServiceResponse<VerifyResponse>, override val svrAuthCredentials: SvrAuthCredentialSet?) : VerifyResponseProcessor(response) {
|
||||
|
||||
fun wrongPin(): Boolean {
|
||||
return error is KeyBackupSystemWrongPinException
|
||||
return error is SvrWrongPinException
|
||||
}
|
||||
|
||||
fun getTokenResponse(): TokenResponse {
|
||||
return (error as KeyBackupSystemWrongPinException).tokenResponse
|
||||
}
|
||||
|
||||
override fun isKbsLocked(): Boolean {
|
||||
return error is KeyBackupSystemNoDataException
|
||||
override fun isRegistrationLockPresentAndSvrExhausted(): Boolean {
|
||||
return error is SvrNoDataException
|
||||
}
|
||||
|
||||
fun updatedIfRegistrationFailed(response: ServiceResponse<VerifyResponse>): VerifyResponseWithRegistrationLockProcessor {
|
||||
@@ -105,12 +97,12 @@ class VerifyResponseWithRegistrationLockProcessor(response: ServiceResponse<Veri
|
||||
return this
|
||||
}
|
||||
|
||||
return VerifyResponseWithRegistrationLockProcessor(ServiceResponse.coerceError(response), tokenData)
|
||||
return VerifyResponseWithRegistrationLockProcessor(ServiceResponse.coerceError(response), svrAuthCredentials)
|
||||
}
|
||||
|
||||
override fun isServerSentError(): Boolean {
|
||||
return super.isServerSentError() ||
|
||||
error is KeyBackupSystemWrongPinException ||
|
||||
error is KeyBackupSystemNoDataException
|
||||
error is SvrWrongPinException ||
|
||||
error is SvrNoDataException
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,11 +172,9 @@ public abstract class BaseEnterSmsCodeFragment<ViewModel extends BaseRegistratio
|
||||
handleSuccessfulVerify();
|
||||
} else if (processor.rateLimit()) {
|
||||
handleRateLimited();
|
||||
} else if (processor.registrationLock() && !processor.isKbsLocked()) {
|
||||
} else if (processor.registrationLock() && !processor.isRegistrationLockPresentAndSvrExhausted()) {
|
||||
LockedException lockedException = processor.getLockedException();
|
||||
handleRegistrationLock(lockedException.getTimeRemaining());
|
||||
} else if (processor.isKbsLocked()) {
|
||||
handleKbsAccountLocked();
|
||||
} else if (processor.authorizationFailed()) {
|
||||
handleIncorrectCodeError();
|
||||
} else {
|
||||
@@ -227,7 +225,7 @@ public abstract class BaseEnterSmsCodeFragment<ViewModel extends BaseRegistratio
|
||||
});
|
||||
}
|
||||
|
||||
protected void handleKbsAccountLocked() {
|
||||
protected void handleSvrAccountLocked() {
|
||||
navigateToKbsAccountLocked();
|
||||
}
|
||||
|
||||
|
||||
@@ -21,12 +21,12 @@ import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.LoggingFragment;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.lock.v2.PinKeyboardType;
|
||||
import org.thoughtcrime.securesms.pin.TokenData;
|
||||
import org.thoughtcrime.securesms.registration.viewmodel.BaseRegistrationViewModel;
|
||||
import org.signal.core.util.concurrent.LifecycleDisposable;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.views.CircularProgressMaterialButton;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
@@ -114,10 +114,9 @@ public abstract class BaseRegistrationLockFragment extends LoggingFragment {
|
||||
viewModel.getLockedTimeRemaining()
|
||||
.observe(getViewLifecycleOwner(), t -> timeRemaining = t);
|
||||
|
||||
TokenData keyBackupCurrentToken = viewModel.getKeyBackupCurrentToken();
|
||||
Integer triesRemaining = viewModel.getSvrTriesRemaining();
|
||||
|
||||
if (keyBackupCurrentToken != null) {
|
||||
int triesRemaining = keyBackupCurrentToken.getTriesRemaining();
|
||||
if (triesRemaining != null) {
|
||||
if (triesRemaining <= 3) {
|
||||
int daysRemaining = getLockoutDays(timeRemaining);
|
||||
|
||||
@@ -177,8 +176,8 @@ public abstract class BaseRegistrationLockFragment extends LoggingFragment {
|
||||
if (processor.hasResult()) {
|
||||
handleSuccessfulPinEntry(pin);
|
||||
} else if (processor.wrongPin()) {
|
||||
onIncorrectKbsRegistrationLockPin(processor.getTokenData());
|
||||
} else if (processor.isKbsLocked() || processor.registrationLock()) {
|
||||
onIncorrectKbsRegistrationLockPin(Objects.requireNonNull(processor.getSvrTriesRemaining()));
|
||||
} else if (processor.isRegistrationLockPresentAndSvrExhausted() || processor.registrationLock()) {
|
||||
onKbsAccountLocked();
|
||||
} else if (processor.rateLimit()) {
|
||||
onRateLimited();
|
||||
@@ -191,35 +190,33 @@ public abstract class BaseRegistrationLockFragment extends LoggingFragment {
|
||||
disposables.add(verify);
|
||||
}
|
||||
|
||||
public void onIncorrectKbsRegistrationLockPin(@NonNull TokenData tokenData) {
|
||||
public void onIncorrectKbsRegistrationLockPin(int svrTriesRemaining) {
|
||||
pinButton.cancelSpinning();
|
||||
pinEntry.getText().clear();
|
||||
enableAndFocusPinEntry();
|
||||
|
||||
viewModel.setKeyBackupTokenData(tokenData);
|
||||
viewModel.setSvrTriesRemaining(svrTriesRemaining);
|
||||
|
||||
int triesRemaining = tokenData.getTriesRemaining();
|
||||
|
||||
if (triesRemaining == 0) {
|
||||
if (svrTriesRemaining == 0) {
|
||||
Log.w(TAG, "Account locked. User out of attempts on KBS.");
|
||||
onAccountLocked();
|
||||
return;
|
||||
}
|
||||
|
||||
if (triesRemaining == 3) {
|
||||
if (svrTriesRemaining == 3) {
|
||||
int daysRemaining = getLockoutDays(timeRemaining);
|
||||
|
||||
new MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.RegistrationLockFragment__incorrect_pin)
|
||||
.setMessage(getTriesRemainingDialogMessage(triesRemaining, daysRemaining))
|
||||
.setMessage(getTriesRemainingDialogMessage(svrTriesRemaining, daysRemaining))
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
if (triesRemaining > 5) {
|
||||
if (svrTriesRemaining > 5) {
|
||||
errorLabel.setText(R.string.RegistrationLockFragment__incorrect_pin_try_again);
|
||||
} else {
|
||||
errorLabel.setText(requireContext().getResources().getQuantityString(R.plurals.RegistrationLockFragment__incorrect_pin_d_attempts_remaining, triesRemaining, triesRemaining));
|
||||
errorLabel.setText(requireContext().getResources().getQuantityString(R.plurals.RegistrationLockFragment__incorrect_pin_d_attempts_remaining, svrTriesRemaining, svrTriesRemaining));
|
||||
forgotPin.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,7 +470,6 @@ public final class EnterPhoneNumberFragment extends LoggingFragment implements R
|
||||
: R.string.RegistrationActivity_a_verification_code_will_be_sent_to_this_number,
|
||||
e164number,
|
||||
() -> {
|
||||
exitInProgressUiState();
|
||||
ViewUtil.hideKeyboard(context, number.getEditText());
|
||||
onConfirmed.run();
|
||||
},
|
||||
|
||||
@@ -14,8 +14,8 @@ import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.LoggingFragment
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.databinding.PinRestoreEntryFragmentBinding
|
||||
import org.thoughtcrime.securesms.lock.v2.KbsConstants
|
||||
import org.thoughtcrime.securesms.lock.v2.PinKeyboardType
|
||||
import org.thoughtcrime.securesms.lock.v2.SvrConstants
|
||||
import org.thoughtcrime.securesms.registration.VerifyResponseWithRegistrationLockProcessor
|
||||
import org.thoughtcrime.securesms.registration.viewmodel.ReRegisterWithPinViewModel
|
||||
import org.thoughtcrime.securesms.registration.viewmodel.RegistrationViewModel
|
||||
@@ -80,7 +80,7 @@ class ReRegisterWithPinFragment : LoggingFragment(R.layout.pin_restore_entry_fra
|
||||
|
||||
binding.pinRestoreKeyboardToggle.setIconResource(getPinEntryKeyboardType().other.iconResource)
|
||||
|
||||
reRegisterViewModel.updateTokenData(registrationViewModel.keyBackupCurrentToken)
|
||||
reRegisterViewModel.updateSvrTriesRemaining(registrationViewModel.svrTriesRemaining)
|
||||
|
||||
disposables += reRegisterViewModel.triesRemaining.subscribe(this::updateTriesRemaining)
|
||||
}
|
||||
@@ -93,7 +93,7 @@ class ReRegisterWithPinFragment : LoggingFragment(R.layout.pin_restore_entry_fra
|
||||
private fun handlePinEntry() {
|
||||
val pin: String? = binding.pinRestorePinInput.text?.toString()
|
||||
|
||||
val trimmedLength = pin?.replace(" ", "")?.length ?: 0
|
||||
val trimmedLength = pin?.trim()?.length ?: 0
|
||||
if (trimmedLength == 0) {
|
||||
Toast.makeText(requireContext(), R.string.RegistrationActivity_you_must_enter_your_registration_lock_PIN, Toast.LENGTH_LONG).show()
|
||||
enableAndFocusPinEntry()
|
||||
@@ -126,12 +126,12 @@ class ReRegisterWithPinFragment : LoggingFragment(R.layout.pin_restore_entry_fra
|
||||
reRegisterViewModel.hasIncorrectGuess = true
|
||||
|
||||
if (processor is VerifyResponseWithRegistrationLockProcessor && processor.wrongPin()) {
|
||||
reRegisterViewModel.updateTokenData(processor.tokenData)
|
||||
if (processor.tokenData != null) {
|
||||
registrationViewModel.setKeyBackupTokenData(processor.tokenData)
|
||||
reRegisterViewModel.updateSvrTriesRemaining(processor.svrTriesRemaining)
|
||||
if (processor.svrTriesRemaining != null) {
|
||||
registrationViewModel.svrTriesRemaining = processor.svrTriesRemaining
|
||||
}
|
||||
return@subscribe
|
||||
} else if (processor.isKbsLocked()) {
|
||||
} else if (processor.isRegistrationLockPresentAndSvrExhausted()) {
|
||||
Log.w(TAG, "Unable to continue skip flow, KBS is locked")
|
||||
onAccountLocked()
|
||||
} else if (processor.isIncorrectRegistrationRecoveryPassword()) {
|
||||
@@ -215,7 +215,7 @@ class ReRegisterWithPinFragment : LoggingFragment(R.layout.pin_restore_entry_fra
|
||||
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.PinRestoreEntryFragment_need_help)
|
||||
.setMessage(getString(message, KbsConstants.MINIMUM_PIN_LENGTH))
|
||||
.setMessage(getString(message, SvrConstants.MINIMUM_PIN_LENGTH))
|
||||
.setPositiveButton(R.string.PinRestoreEntryFragment_skip) { _, _ -> onSkipPinEntry() }
|
||||
.setNeutralButton(R.string.PinRestoreEntryFragment_contact_support) { _, _ ->
|
||||
val body = SupportEmailUtil.generateSupportEmailBody(requireContext(), R.string.ReRegisterWithPinFragment_support_email_subject, null, null)
|
||||
|
||||
@@ -16,7 +16,7 @@ import org.thoughtcrime.securesms.jobs.MultiDeviceProfileContentUpdateJob
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceProfileKeyUpdateJob
|
||||
import org.thoughtcrime.securesms.jobs.ProfileUploadJob
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.lock.v2.CreateKbsPinActivity
|
||||
import org.thoughtcrime.securesms.lock.v2.CreateSvrPinActivity
|
||||
import org.thoughtcrime.securesms.pin.PinRestoreActivity
|
||||
import org.thoughtcrime.securesms.profiles.AvatarHelper
|
||||
import org.thoughtcrime.securesms.profiles.edit.EditProfileActivity
|
||||
@@ -51,7 +51,7 @@ class RegistrationCompleteFragment : LoggingFragment() {
|
||||
val isProfileNameEmpty = Recipient.self().profileName.isEmpty
|
||||
val isAvatarEmpty = !AvatarHelper.hasAvatar(activity, Recipient.self().id)
|
||||
val needsProfile = isProfileNameEmpty || isAvatarEmpty
|
||||
val needsPin = !SignalStore.kbsValues().hasPin() && !viewModel.isReregister
|
||||
val needsPin = !SignalStore.svr().hasPin() && !viewModel.isReregister
|
||||
|
||||
Log.i(TAG, "Pin restore flow not required. Profile name: $isProfileNameEmpty | Profile avatar: $isAvatarEmpty | Needs PIN: $needsPin")
|
||||
|
||||
@@ -66,7 +66,7 @@ class RegistrationCompleteFragment : LoggingFragment() {
|
||||
var startIntent = MainActivity.clearTop(activity)
|
||||
|
||||
if (needsPin) {
|
||||
startIntent = chainIntents(CreateKbsPinActivity.getIntentForPinCreate(activity), startIntent)
|
||||
startIntent = chainIntents(CreateSvrPinActivity.getIntentForPinCreate(activity), startIntent)
|
||||
}
|
||||
|
||||
if (needsProfile) {
|
||||
|
||||
@@ -58,7 +58,7 @@ object RegistrationViewDelegate {
|
||||
setMessage(message)
|
||||
setPositiveButton(android.R.string.ok) { _, _ -> onConfirmed.run() }
|
||||
setNegativeButton(R.string.RegistrationActivity_edit_number) { _, _ -> onEditNumber.run() }
|
||||
setOnDismissListener { onEditNumber.run() }
|
||||
setOnCancelListener { onEditNumber.run() }
|
||||
}.show()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,16 +12,13 @@ import com.google.i18n.phonenumbers.Phonenumber;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.pin.KbsRepository;
|
||||
import org.thoughtcrime.securesms.pin.TokenData;
|
||||
import org.thoughtcrime.securesms.registration.RegistrationSessionProcessor;
|
||||
import org.thoughtcrime.securesms.registration.VerifyAccountRepository;
|
||||
import org.thoughtcrime.securesms.registration.VerifyAccountRepository.Mode;
|
||||
import org.thoughtcrime.securesms.registration.VerifyResponse;
|
||||
import org.thoughtcrime.securesms.registration.VerifyResponseProcessor;
|
||||
import org.thoughtcrime.securesms.registration.VerifyResponseWithFailedKbs;
|
||||
import org.thoughtcrime.securesms.registration.VerifyResponseWithRegistrationLockProcessor;
|
||||
import org.thoughtcrime.securesms.registration.VerifyResponseWithSuccessfulKbs;
|
||||
import org.thoughtcrime.securesms.registration.VerifyResponseHitRegistrationLock;
|
||||
import org.thoughtcrime.securesms.registration.VerifyResponseWithoutKbs;
|
||||
import org.whispersystems.signalservice.internal.ServiceResponse;
|
||||
|
||||
@@ -48,7 +45,8 @@ public abstract class BaseRegistrationViewModel extends ViewModel {
|
||||
private static final String STATE_PUSH_TIMED_OUT = "PUSH_TIMED_OUT";
|
||||
private static final String STATE_INCORRECT_CODE_ATTEMPTS = "STATE_INCORRECT_CODE_ATTEMPTS";
|
||||
private static final String STATE_REQUEST_RATE_LIMITER = "REQUEST_RATE_LIMITER";
|
||||
private static final String STATE_KBS_TOKEN = "KBS_TOKEN";
|
||||
private static final String STATE_SVR_AUTH = "SVR_AUTH";
|
||||
private static final String STATE_SVR_TRIES_REMAINING = "SVR_TRIES_REMAINING";
|
||||
private static final String STATE_TIME_REMAINING = "TIME_REMAINING";
|
||||
private static final String STATE_CAN_CALL_AT_TIME = "CAN_CALL_AT_TIME";
|
||||
private static final String STATE_CAN_SMS_AT_TIME = "CAN_SMS_AT_TIME";
|
||||
@@ -56,24 +54,21 @@ public abstract class BaseRegistrationViewModel extends ViewModel {
|
||||
|
||||
protected final SavedStateHandle savedState;
|
||||
protected final VerifyAccountRepository verifyAccountRepository;
|
||||
protected final KbsRepository kbsRepository;
|
||||
|
||||
public BaseRegistrationViewModel(@NonNull SavedStateHandle savedStateHandle,
|
||||
@NonNull VerifyAccountRepository verifyAccountRepository,
|
||||
@NonNull KbsRepository kbsRepository,
|
||||
@NonNull String password)
|
||||
{
|
||||
this.savedState = savedStateHandle;
|
||||
|
||||
this.verifyAccountRepository = verifyAccountRepository;
|
||||
this.kbsRepository = kbsRepository;
|
||||
|
||||
setInitialDefaultValue(STATE_NUMBER, NumberViewState.INITIAL);
|
||||
setInitialDefaultValue(STATE_REGISTRATION_SECRET, password);
|
||||
setInitialDefaultValue(STATE_VERIFICATION_CODE, "");
|
||||
setInitialDefaultValue(STATE_INCORRECT_CODE_ATTEMPTS, 0);
|
||||
setInitialDefaultValue(STATE_REQUEST_RATE_LIMITER, new LocalCodeRequestRateLimiter(60_000));
|
||||
setInitialDefaultValue(STATE_RECOVERY_PASSWORD, SignalStore.kbsValues().getRecoveryPassword());
|
||||
setInitialDefaultValue(STATE_RECOVERY_PASSWORD, SignalStore.svr().getRecoveryPassword());
|
||||
setInitialDefaultValue(STATE_PUSH_TIMED_OUT, false);
|
||||
}
|
||||
|
||||
@@ -188,12 +183,20 @@ public abstract class BaseRegistrationViewModel extends ViewModel {
|
||||
return challengeKeys;
|
||||
}
|
||||
|
||||
public @Nullable TokenData getKeyBackupCurrentToken() {
|
||||
return savedState.get(STATE_KBS_TOKEN);
|
||||
protected void setSvrAuthCredentials(SvrAuthCredentialSet credentials) {
|
||||
savedState.set(STATE_SVR_AUTH, credentials);
|
||||
}
|
||||
|
||||
public void setKeyBackupTokenData(@Nullable TokenData tokenData) {
|
||||
savedState.set(STATE_KBS_TOKEN, tokenData);
|
||||
protected @Nullable SvrAuthCredentialSet getSvrAuthCredentials() {
|
||||
return savedState.get(STATE_SVR_AUTH);
|
||||
}
|
||||
|
||||
public @Nullable Integer getSvrTriesRemaining() {
|
||||
return savedState.get(STATE_SVR_TRIES_REMAINING);
|
||||
}
|
||||
|
||||
public void setSvrTriesRemaining(@Nullable Integer triesRemaining) {
|
||||
savedState.set(STATE_SVR_TRIES_REMAINING, triesRemaining);
|
||||
}
|
||||
|
||||
public void setRecoveryPassword(@Nullable String recoveryPassword) {
|
||||
@@ -338,56 +341,54 @@ public abstract class BaseRegistrationViewModel extends ViewModel {
|
||||
|
||||
return verifyAccountWithoutRegistrationLock()
|
||||
.flatMap(response -> {
|
||||
if (response.getResult().isPresent() && response.getResult().get().getKbsData() != null) {
|
||||
if (response.getResult().isPresent() && response.getResult().get().getMasterKey() != null) {
|
||||
return onVerifySuccessWithRegistrationLock(new VerifyResponseWithRegistrationLockProcessor(response, null), response.getResult().get().getPin());
|
||||
}
|
||||
|
||||
VerifyResponseProcessor processor = new VerifyResponseWithoutKbs(response);
|
||||
if (processor.hasResult()) {
|
||||
return onVerifySuccess(processor);
|
||||
} else if (processor.registrationLock() && !processor.isKbsLocked()) {
|
||||
return kbsRepository.getToken(processor.getLockedException().getBasicStorageCredentials())
|
||||
.map(r -> r.getResult().isPresent() ? new VerifyResponseWithSuccessfulKbs(processor.getResponse(), r.getResult().get())
|
||||
: new VerifyResponseWithFailedKbs(r));
|
||||
} else if (processor.registrationLock() && !processor.isRegistrationLockPresentAndSvrExhausted()) {
|
||||
return Single.just(new VerifyResponseHitRegistrationLock(processor.getResponse()));
|
||||
}
|
||||
return Single.just(processor);
|
||||
})
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnSuccess(processor -> {
|
||||
if (processor.registrationLock() && !processor.isKbsLocked()) {
|
||||
if (processor.registrationLock() && !processor.isRegistrationLockPresentAndSvrExhausted()) {
|
||||
setLockedTimeRemaining(processor.getLockedException().getTimeRemaining());
|
||||
setKeyBackupTokenData(processor.getTokenData());
|
||||
} else if (processor.isKbsLocked()) {
|
||||
setSvrTriesRemaining(processor.getSvrTriesRemaining());
|
||||
setSvrAuthCredentials(processor.getSvrAuthCredentials());
|
||||
} else if (processor.isRegistrationLockPresentAndSvrExhausted()) {
|
||||
setLockedTimeRemaining(processor.getLockedException().getTimeRemaining());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Single<VerifyResponseWithRegistrationLockProcessor> verifyCodeAndRegisterAccountWithRegistrationLock(@NonNull String pin) {
|
||||
TokenData kbsTokenData = Objects.requireNonNull(getKeyBackupCurrentToken());
|
||||
SvrAuthCredentialSet authCredentials = Objects.requireNonNull(getSvrAuthCredentials());
|
||||
|
||||
return verifyAccountWithRegistrationLock(pin, kbsTokenData)
|
||||
.map(r -> new VerifyResponseWithRegistrationLockProcessor(r, kbsTokenData))
|
||||
return verifyAccountWithRegistrationLock(pin, authCredentials)
|
||||
.map(r -> new VerifyResponseWithRegistrationLockProcessor(r, authCredentials))
|
||||
.flatMap(processor -> {
|
||||
if (processor.hasResult()) {
|
||||
return onVerifySuccessWithRegistrationLock(processor, pin);
|
||||
} else if (processor.wrongPin()) {
|
||||
TokenData newToken = TokenData.withResponse(kbsTokenData, processor.getTokenResponse());
|
||||
return Single.just(new VerifyResponseWithRegistrationLockProcessor(processor.getResponse(), newToken));
|
||||
return Single.just(new VerifyResponseWithRegistrationLockProcessor(processor.getResponse(), authCredentials));
|
||||
}
|
||||
return Single.just(processor);
|
||||
})
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnSuccess(processor -> {
|
||||
if (processor.wrongPin()) {
|
||||
setKeyBackupTokenData(processor.getTokenData());
|
||||
setSvrTriesRemaining(processor.getSvrTriesRemaining());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected abstract Single<ServiceResponse<VerifyResponse>> verifyAccountWithoutRegistrationLock();
|
||||
|
||||
protected abstract Single<ServiceResponse<VerifyResponse>> verifyAccountWithRegistrationLock(@NonNull String pin, @NonNull TokenData kbsTokenData);
|
||||
protected abstract Single<ServiceResponse<VerifyResponse>> verifyAccountWithRegistrationLock(@NonNull String pin, @NonNull SvrAuthCredentialSet svrAuthCredentials);
|
||||
|
||||
protected abstract Single<VerifyResponseProcessor> onVerifySuccess(@NonNull VerifyResponseProcessor processor);
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import androidx.lifecycle.ViewModel
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import io.reactivex.rxjava3.subjects.BehaviorSubject
|
||||
import org.thoughtcrime.securesms.pin.TokenData
|
||||
|
||||
/**
|
||||
* Used during re-registration flow when pin entry is required to skip SMS verification. Mostly tracks
|
||||
@@ -19,14 +18,14 @@ class ReRegisterWithPinViewModel : ViewModel() {
|
||||
private val _triesRemaining: BehaviorSubject<Int> = BehaviorSubject.createDefault(10)
|
||||
val triesRemaining: Observable<Int> = _triesRemaining.observeOn(AndroidSchedulers.mainThread())
|
||||
|
||||
fun updateTokenData(tokenData: TokenData?) {
|
||||
if (tokenData == null) {
|
||||
fun updateSvrTriesRemaining(triesRemaining: Int?) {
|
||||
if (triesRemaining == null) {
|
||||
isLocalVerification = true
|
||||
if (hasIncorrectGuess) {
|
||||
_triesRemaining.onNext((_triesRemaining.value!! - 1).coerceAtLeast(0))
|
||||
}
|
||||
} else {
|
||||
_triesRemaining.onNext(tokenData.triesRemaining)
|
||||
_triesRemaining.onNext(triesRemaining)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,8 @@ import org.thoughtcrime.securesms.jobs.NewRegistrationUsernameSyncJob;
|
||||
import org.thoughtcrime.securesms.jobs.StorageAccountRestoreJob;
|
||||
import org.thoughtcrime.securesms.jobs.StorageSyncJob;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.pin.KbsRepository;
|
||||
import org.thoughtcrime.securesms.pin.KeyBackupSystemWrongPinException;
|
||||
import org.thoughtcrime.securesms.pin.TokenData;
|
||||
import org.thoughtcrime.securesms.pin.SvrWrongPinException;
|
||||
import org.thoughtcrime.securesms.pin.SvrRepository;
|
||||
import org.thoughtcrime.securesms.registration.RegistrationData;
|
||||
import org.thoughtcrime.securesms.registration.RegistrationRepository;
|
||||
import org.thoughtcrime.securesms.registration.RegistrationSessionProcessor;
|
||||
@@ -29,13 +28,13 @@ import org.thoughtcrime.securesms.registration.VerifyResponseWithRegistrationLoc
|
||||
import org.thoughtcrime.securesms.registration.VerifyResponseWithoutKbs;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.signalservice.api.KbsPinData;
|
||||
import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException;
|
||||
import org.whispersystems.signalservice.api.SvrNoDataException;
|
||||
import org.whispersystems.signalservice.api.kbs.MasterKey;
|
||||
import org.whispersystems.signalservice.api.kbs.PinHashUtil;
|
||||
import org.whispersystems.signalservice.api.push.ServiceIdType;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.IncorrectCodeException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.IncorrectRegistrationRecoveryPasswordException;
|
||||
import org.whispersystems.signalservice.internal.ServiceResponse;
|
||||
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
|
||||
import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse;
|
||||
import org.whispersystems.util.Base64;
|
||||
|
||||
@@ -67,10 +66,9 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
||||
public RegistrationViewModel(@NonNull SavedStateHandle savedStateHandle,
|
||||
boolean isReregister,
|
||||
@NonNull VerifyAccountRepository verifyAccountRepository,
|
||||
@NonNull KbsRepository kbsRepository,
|
||||
@NonNull RegistrationRepository registrationRepository)
|
||||
{
|
||||
super(savedStateHandle, verifyAccountRepository, kbsRepository, Util.getSecret(18));
|
||||
super(savedStateHandle, verifyAccountRepository, Util.getSecret(18));
|
||||
|
||||
this.registrationRepository = registrationRepository;
|
||||
|
||||
@@ -174,14 +172,12 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
||||
})
|
||||
.flatMap(verifyAccountWithoutKbsResponse -> {
|
||||
VerifyResponseProcessor processor = new VerifyResponseWithoutKbs(verifyAccountWithoutKbsResponse);
|
||||
String pin = SignalStore.kbsValues().getPin();
|
||||
String pin = SignalStore.svr().getPin();
|
||||
|
||||
if ((processor.isKbsLocked() || processor.registrationLock()) && SignalStore.kbsValues().getRegistrationLockToken() != null && pin != null) {
|
||||
KbsPinData pinData = new KbsPinData(SignalStore.kbsValues().getOrCreateMasterKey(), SignalStore.kbsValues().getRegistrationLockTokenResponse());
|
||||
|
||||
return verifyAccountRepository.registerAccount(sessionId, getRegistrationData(), pin, () -> pinData)
|
||||
if ((processor.isRegistrationLockPresentAndSvrExhausted() || processor.registrationLock()) && SignalStore.svr().getRegistrationLockToken() != null && pin != null) {
|
||||
return verifyAccountRepository.registerAccount(sessionId, getRegistrationData(), pin, () -> SignalStore.svr().getOrCreateMasterKey())
|
||||
.map(verifyAccountWithPinResponse -> {
|
||||
if (verifyAccountWithPinResponse.getResult().isPresent() && verifyAccountWithPinResponse.getResult().get().getKbsData() != null) {
|
||||
if (verifyAccountWithPinResponse.getResult().isPresent() && verifyAccountWithPinResponse.getResult().get().getMasterKey() != null) {
|
||||
return verifyAccountWithPinResponse;
|
||||
} else {
|
||||
return verifyAccountWithoutKbsResponse;
|
||||
@@ -195,7 +191,7 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Single<ServiceResponse<VerifyResponse>> verifyAccountWithRegistrationLock(@NonNull String pin, @NonNull TokenData kbsTokenData) {
|
||||
protected Single<ServiceResponse<VerifyResponse>> verifyAccountWithRegistrationLock(@NonNull String pin, @NonNull SvrAuthCredentialSet svrAuthCredentials) {
|
||||
final String sessionId = getSessionId();
|
||||
if (sessionId == null) {
|
||||
throw new IllegalStateException("No valid registration session");
|
||||
@@ -210,7 +206,7 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
||||
})
|
||||
.<ServiceResponse<VerifyResponse>>flatMap(processor -> {
|
||||
if (processor.isAlreadyVerified() || (processor.hasResult() && processor.isVerified())) {
|
||||
return verifyAccountRepository.registerAccount(sessionId, getRegistrationData(), pin, () -> Objects.requireNonNull(KbsRepository.restoreMasterKey(pin, kbsTokenData.getEnclave(), kbsTokenData.getBasicAuth(), kbsTokenData.getTokenResponse())));
|
||||
return verifyAccountRepository.registerAccount(sessionId, getRegistrationData(), pin, () -> SvrRepository.restoreMasterKeyPreRegistration(svrAuthCredentials, pin));
|
||||
} else {
|
||||
return Single.just(ServiceResponse.coerceError(processor.getResponse()));
|
||||
}
|
||||
@@ -250,18 +246,17 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
||||
return updateFcmTokenValue().subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.io())
|
||||
.onErrorReturnItem("")
|
||||
.flatMap(s -> verifyReRegisterWithRecoveryPassword(pin, data.pinData));
|
||||
.flatMap(s -> verifyReRegisterWithRecoveryPassword(pin, data.masterKey));
|
||||
} else {
|
||||
throw new IllegalStateException("Unable to get token or master key");
|
||||
throw new IncorrectRegistrationRecoveryPasswordException();
|
||||
}
|
||||
})
|
||||
.onErrorReturn(t -> new VerifyResponseWithRegistrationLockProcessor(ServiceResponse.forUnknownError(t), getKeyBackupCurrentToken()))
|
||||
.onErrorReturn(t -> new VerifyResponseWithRegistrationLockProcessor(ServiceResponse.forUnknownError(t), getSvrAuthCredentials()))
|
||||
.map(p -> {
|
||||
if (p instanceof VerifyResponseWithRegistrationLockProcessor) {
|
||||
VerifyResponseWithRegistrationLockProcessor lockProcessor = (VerifyResponseWithRegistrationLockProcessor) p;
|
||||
if (lockProcessor.wrongPin() && lockProcessor.getTokenData() != null) {
|
||||
TokenData newToken = TokenData.withResponse(lockProcessor.getTokenData(), lockProcessor.getTokenResponse());
|
||||
return new VerifyResponseWithRegistrationLockProcessor(lockProcessor.getResponse(), newToken);
|
||||
if (lockProcessor.wrongPin() && lockProcessor.getSvrTriesRemaining() != null) {
|
||||
return new VerifyResponseWithRegistrationLockProcessor(lockProcessor.getResponse(), lockProcessor.getSvrAuthCredentials());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,37 +272,33 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
||||
|
||||
@WorkerThread
|
||||
private @NonNull ReRegistrationData verifyReRegisterWithPinInternal(@NonNull String pin)
|
||||
throws KeyBackupSystemWrongPinException, IOException, KeyBackupSystemNoDataException
|
||||
throws SvrWrongPinException, IOException, SvrNoDataException
|
||||
{
|
||||
String localPinHash = SignalStore.kbsValues().getLocalPinHash();
|
||||
String localPinHash = SignalStore.svr().getLocalPinHash();
|
||||
|
||||
if (hasRecoveryPassword() && localPinHash != null) {
|
||||
if (PinHashUtil.verifyLocalPinHash(localPinHash, pin)) {
|
||||
Log.i(TAG, "Local pin matches input, attempting registration");
|
||||
return ReRegistrationData.canProceed(new KbsPinData(SignalStore.kbsValues().getOrCreateMasterKey(), SignalStore.kbsValues().getRegistrationLockTokenResponse()));
|
||||
return ReRegistrationData.canProceed(SignalStore.svr().getOrCreateMasterKey());
|
||||
} else {
|
||||
throw new KeyBackupSystemWrongPinException(new TokenResponse(null, null, 0));
|
||||
throw new SvrWrongPinException(0);
|
||||
}
|
||||
} else {
|
||||
TokenData data = getKeyBackupCurrentToken();
|
||||
if (data == null) {
|
||||
Log.w(TAG, "No token data, abort skip flow");
|
||||
SvrAuthCredentialSet authCredentials = getSvrAuthCredentials();
|
||||
if (authCredentials == null) {
|
||||
Log.w(TAG, "No SVR auth credentials, abort skip flow");
|
||||
return ReRegistrationData.cannotProceed();
|
||||
}
|
||||
|
||||
KbsPinData kbsPinData = KbsRepository.restoreMasterKey(pin, data.getEnclave(), data.getBasicAuth(), data.getTokenResponse());
|
||||
if (kbsPinData == null || kbsPinData.getMasterKey() == null) {
|
||||
Log.w(TAG, "No kbs data, abort skip flow");
|
||||
return ReRegistrationData.cannotProceed();
|
||||
}
|
||||
MasterKey masterKey = SvrRepository.restoreMasterKeyPreRegistration(authCredentials, pin);
|
||||
|
||||
setRecoveryPassword(kbsPinData.getMasterKey().deriveRegistrationRecoveryPassword());
|
||||
setKeyBackupTokenData(data);
|
||||
return ReRegistrationData.canProceed(kbsPinData);
|
||||
setRecoveryPassword(masterKey.deriveRegistrationRecoveryPassword());
|
||||
setSvrTriesRemaining(10);
|
||||
return ReRegistrationData.canProceed(masterKey);
|
||||
}
|
||||
}
|
||||
|
||||
private Single<VerifyResponseProcessor> verifyReRegisterWithRecoveryPassword(@NonNull String pin, @NonNull KbsPinData pinData) {
|
||||
private Single<VerifyResponseProcessor> verifyReRegisterWithRecoveryPassword(@NonNull String pin, @NonNull MasterKey masterKey) {
|
||||
RegistrationData registrationData = getRegistrationData();
|
||||
if (registrationData.getRecoveryPassword() == null) {
|
||||
throw new IllegalStateException("No valid recovery password");
|
||||
@@ -319,9 +310,10 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
||||
.map(VerifyResponseWithoutKbs::new)
|
||||
.flatMap(processor -> {
|
||||
if (processor.registrationLock()) {
|
||||
return verifyAccountRepository.registerAccount(null, registrationData, pin, () -> pinData)
|
||||
setSvrAuthCredentials(processor.getSvrAuthCredentials());
|
||||
return verifyAccountRepository.registerAccount(null, registrationData, pin, () -> masterKey)
|
||||
.onErrorReturn(ServiceResponse::forUnknownError)
|
||||
.map(r -> new VerifyResponseWithRegistrationLockProcessor(r, getKeyBackupCurrentToken()));
|
||||
.map(r -> new VerifyResponseWithRegistrationLockProcessor(r, processor.getSvrAuthCredentials()));
|
||||
} else {
|
||||
return Single.just(processor);
|
||||
}
|
||||
@@ -329,14 +321,14 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
||||
.flatMap(processor -> {
|
||||
if (processor.hasResult()) {
|
||||
VerifyResponse verifyResponse = processor.getResult();
|
||||
boolean setRegistrationLockEnabled = verifyResponse.getKbsData() != null;
|
||||
boolean setRegistrationLockEnabled = verifyResponse.getMasterKey() != null;
|
||||
|
||||
if (!setRegistrationLockEnabled) {
|
||||
verifyResponse = new VerifyResponse(processor.getResult().getVerifyAccountResponse(), pinData, pin, verifyResponse.getAciPreKeyCollection(), verifyResponse.getPniPreKeyCollection());
|
||||
verifyResponse = new VerifyResponse(processor.getResult().getVerifyAccountResponse(), masterKey, pin, verifyResponse.getAciPreKeyCollection(), verifyResponse.getPniPreKeyCollection());
|
||||
}
|
||||
|
||||
return registrationRepository.registerAccount(registrationData, verifyResponse, setRegistrationLockEnabled)
|
||||
.map(r -> new VerifyResponseWithRegistrationLockProcessor(r, getKeyBackupCurrentToken()));
|
||||
.map(r -> new VerifyResponseWithRegistrationLockProcessor(r, getSvrAuthCredentials()));
|
||||
} else {
|
||||
return Single.just(processor);
|
||||
}
|
||||
@@ -360,8 +352,8 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
||||
}
|
||||
|
||||
private Single<Boolean> checkForValidKbsAuthCredentials() {
|
||||
final List<String> kbsAuthTokenList = SignalStore.kbsValues().getKbsAuthTokenList();
|
||||
List<String> usernamePasswords = kbsAuthTokenList
|
||||
final List<String> svrAuthTokenList = SignalStore.svr().getAuthTokenList();
|
||||
List<String> usernamePasswords = svrAuthTokenList
|
||||
.stream()
|
||||
.limit(10)
|
||||
.map(t -> {
|
||||
@@ -377,23 +369,8 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
||||
return Single.just(false);
|
||||
}
|
||||
|
||||
return registrationRepository.getKbsAuthCredential(getRegistrationData(), usernamePasswords)
|
||||
.flatMap(p -> {
|
||||
if (p.getValid() != null) {
|
||||
return kbsRepository.getToken(p.getValid())
|
||||
.flatMap(r -> {
|
||||
if (r.getResult().isPresent()) {
|
||||
TokenData tokenData = r.getResult().get();
|
||||
setKeyBackupTokenData(tokenData);
|
||||
return Single.just(tokenData.getTriesRemaining() > 0);
|
||||
} else {
|
||||
return Single.just(false);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return Single.just(false);
|
||||
}
|
||||
})
|
||||
return registrationRepository.getSvrAuthCredential(getRegistrationData(), usernamePasswords)
|
||||
.flatMap(p -> Single.just(p.getValid() != null))
|
||||
.onErrorReturnItem(false)
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
@@ -432,20 +409,20 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
||||
}
|
||||
|
||||
private static class ReRegistrationData {
|
||||
public boolean canProceed;
|
||||
public KbsPinData pinData;
|
||||
public boolean canProceed;
|
||||
public MasterKey masterKey;
|
||||
|
||||
private ReRegistrationData(boolean canProceed, @Nullable KbsPinData pinData) {
|
||||
private ReRegistrationData(boolean canProceed, @Nullable MasterKey masterKey) {
|
||||
this.canProceed = canProceed;
|
||||
this.pinData = pinData;
|
||||
this.masterKey = masterKey;
|
||||
}
|
||||
|
||||
public static ReRegistrationData cannotProceed() {
|
||||
return new ReRegistrationData(false, null);
|
||||
}
|
||||
|
||||
public static ReRegistrationData canProceed(@NonNull KbsPinData pinData) {
|
||||
return new ReRegistrationData(true, pinData);
|
||||
public static ReRegistrationData canProceed(@NonNull MasterKey masterKey) {
|
||||
return new ReRegistrationData(true, masterKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,7 +440,6 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
||||
return modelClass.cast(new RegistrationViewModel(handle,
|
||||
isReregister,
|
||||
new VerifyAccountRepository(ApplicationDependencies.getApplication()),
|
||||
new KbsRepository(),
|
||||
new RegistrationRepository(ApplicationDependencies.getApplication())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.registration.viewmodel
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.whispersystems.signalservice.internal.push.AuthCredentials
|
||||
|
||||
@Parcelize
|
||||
data class SvrAuthCredentialSet(
|
||||
private val svr1Credentials: ParcelableAuthCredentials?,
|
||||
private val svr2Credentials: ParcelableAuthCredentials?
|
||||
) : Parcelable {
|
||||
constructor(
|
||||
svr1Credentials: AuthCredentials?,
|
||||
svr2Credentials: AuthCredentials?
|
||||
) : this(ParcelableAuthCredentials.createOrNull(svr1Credentials), ParcelableAuthCredentials.createOrNull(svr2Credentials))
|
||||
|
||||
val svr1: AuthCredentials? = svr1Credentials?.credentials()
|
||||
val svr2: AuthCredentials? = svr2Credentials?.credentials()
|
||||
|
||||
@Parcelize
|
||||
data class ParcelableAuthCredentials(private val username: String, private val password: String) : Parcelable {
|
||||
|
||||
companion object {
|
||||
fun createOrNull(creds: AuthCredentials?): ParcelableAuthCredentials? {
|
||||
return if (creds != null) {
|
||||
ParcelableAuthCredentials(creds.username(), creds.password())
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun credentials(): AuthCredentials {
|
||||
return AuthCredentials.create(username, password)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user