mirror of
https://github.com/oxen-io/session-android.git
synced 2025-06-09 11:08:33 +00:00
Handle presenting KBS account locked cases.
This commit is contained in:
parent
e14861d79d
commit
40383f3733
@ -50,6 +50,7 @@ import org.thoughtcrime.securesms.util.ThemeUtil;
|
|||||||
import org.thoughtcrime.securesms.util.text.AfterTextChanged;
|
import org.thoughtcrime.securesms.util.text.AfterTextChanged;
|
||||||
import org.whispersystems.signalservice.api.KeyBackupService;
|
import org.whispersystems.signalservice.api.KeyBackupService;
|
||||||
import org.whispersystems.signalservice.api.KeyBackupServicePinException;
|
import org.whispersystems.signalservice.api.KeyBackupServicePinException;
|
||||||
|
import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException;
|
||||||
import org.whispersystems.signalservice.api.RegistrationLockData;
|
import org.whispersystems.signalservice.api.RegistrationLockData;
|
||||||
import org.whispersystems.signalservice.api.kbs.HashedPin;
|
import org.whispersystems.signalservice.api.kbs.HashedPin;
|
||||||
import org.whispersystems.signalservice.api.kbs.MasterKey;
|
import org.whispersystems.signalservice.api.kbs.MasterKey;
|
||||||
@ -305,7 +306,7 @@ public final class RegistrationLockDialog {
|
|||||||
TextSecurePreferences.setRegistrationLockLastReminderTime(context, System.currentTimeMillis());
|
TextSecurePreferences.setRegistrationLockLastReminderTime(context, System.currentTimeMillis());
|
||||||
TextSecurePreferences.setRegistrationLockNextReminderInterval(context, RegistrationLockReminders.INITIAL_INTERVAL);
|
TextSecurePreferences.setRegistrationLockNextReminderInterval(context, RegistrationLockReminders.INITIAL_INTERVAL);
|
||||||
return true;
|
return true;
|
||||||
} catch (IOException | UnauthenticatedResponseException | KeyBackupServicePinException e) {
|
} catch (IOException | UnauthenticatedResponseException | KeyBackupServicePinException | KeyBackupSystemNoDataException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|||||||
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
||||||
import org.whispersystems.signalservice.api.KeyBackupService;
|
import org.whispersystems.signalservice.api.KeyBackupService;
|
||||||
import org.whispersystems.signalservice.api.KeyBackupServicePinException;
|
import org.whispersystems.signalservice.api.KeyBackupServicePinException;
|
||||||
|
import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException;
|
||||||
import org.whispersystems.signalservice.api.RegistrationLockData;
|
import org.whispersystems.signalservice.api.RegistrationLockData;
|
||||||
import org.whispersystems.signalservice.api.kbs.HashedPin;
|
import org.whispersystems.signalservice.api.kbs.HashedPin;
|
||||||
import org.whispersystems.signalservice.api.kbs.MasterKey;
|
import org.whispersystems.signalservice.api.kbs.MasterKey;
|
||||||
@ -59,7 +60,7 @@ final class ConfirmKbsPinRepository {
|
|||||||
ApplicationDependencies.getMegaphoneRepository().markFinished(Megaphones.Event.PINS_FOR_ALL);
|
ApplicationDependencies.getMegaphoneRepository().markFinished(Megaphones.Event.PINS_FOR_ALL);
|
||||||
|
|
||||||
return PinSetResult.SUCCESS;
|
return PinSetResult.SUCCESS;
|
||||||
} catch (IOException | UnauthenticatedResponseException | KeyBackupServicePinException e) {
|
} catch (IOException | UnauthenticatedResponseException | KeyBackupServicePinException | KeyBackupSystemNoDataException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
return PinSetResult.FAILURE;
|
return PinSetResult.FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import org.thoughtcrime.securesms.logging.Log;
|
|||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.whispersystems.signalservice.api.KeyBackupService;
|
import org.whispersystems.signalservice.api.KeyBackupService;
|
||||||
import org.whispersystems.signalservice.api.KeyBackupServicePinException;
|
import org.whispersystems.signalservice.api.KeyBackupServicePinException;
|
||||||
|
import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException;
|
||||||
import org.whispersystems.signalservice.api.RegistrationLockData;
|
import org.whispersystems.signalservice.api.RegistrationLockData;
|
||||||
import org.whispersystems.signalservice.api.kbs.HashedPin;
|
import org.whispersystems.signalservice.api.kbs.HashedPin;
|
||||||
import org.whispersystems.signalservice.api.kbs.MasterKey;
|
import org.whispersystems.signalservice.api.kbs.MasterKey;
|
||||||
@ -55,7 +56,7 @@ public final class RegistrationPinV2MigrationJob extends BaseJob {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onRun() throws IOException, UnauthenticatedResponseException, KeyBackupServicePinException {
|
protected void onRun() throws IOException, UnauthenticatedResponseException, KeyBackupServicePinException, KeyBackupSystemNoDataException {
|
||||||
if (!TextSecurePreferences.isV1RegistrationLockEnabled(context)) {
|
if (!TextSecurePreferences.isV1RegistrationLockEnabled(context)) {
|
||||||
Log.i(TAG, "Registration lock disabled");
|
Log.i(TAG, "Registration lock disabled");
|
||||||
return;
|
return;
|
||||||
|
@ -11,11 +11,12 @@ import android.widget.TextView;
|
|||||||
import androidx.activity.OnBackPressedCallback;
|
import androidx.activity.OnBackPressedCallback;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
|
|
||||||
public class AccountLockedFragment extends Fragment {
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class AccountLockedFragment extends BaseRegistrationFragment {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
@ -24,14 +25,16 @@ public class AccountLockedFragment extends Fragment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
AccountLockedFragmentArgs args = AccountLockedFragmentArgs.fromBundle(requireArguments());
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
TextView description = view.findViewById(R.id.account_locked_description);
|
TextView description = view.findViewById(R.id.account_locked_description);
|
||||||
|
|
||||||
description.setText(getString(R.string.AccountLockedFragment__your_account_has_been_locked_to_protect_your_privacy, args.getTimeRemaining()));
|
getModel().getTimeRemaining().observe(getViewLifecycleOwner(),
|
||||||
|
t -> description.setText(getString(R.string.AccountLockedFragment__your_account_has_been_locked_to_protect_your_privacy, durationToDays(t)))
|
||||||
|
);
|
||||||
|
|
||||||
view.findViewById(R.id.account_locked_next).setOnClickListener(this::onNextClicked);
|
view.findViewById(R.id.account_locked_next).setOnClickListener(v -> onNext());
|
||||||
view.findViewById(R.id.account_locked_learn_more).setOnClickListener(this::onLearnMoreClicked);
|
view.findViewById(R.id.account_locked_learn_more).setOnClickListener(v -> learnMore());
|
||||||
|
|
||||||
requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), new OnBackPressedCallback(true) {
|
requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), new OnBackPressedCallback(true) {
|
||||||
@Override
|
@Override
|
||||||
@ -41,16 +44,18 @@ public class AccountLockedFragment extends Fragment {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onNextClicked(@NonNull View unused) {
|
private void learnMore() {
|
||||||
onNext();
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
|
intent.setData(Uri.parse(getString(R.string.AccountLockedFragment__learn_more_url)));
|
||||||
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onLearnMoreClicked(@NonNull View unused) {
|
private static long durationToDays(Long duration) {
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
return duration != null ? getLockoutDays(duration) : 7;
|
||||||
|
}
|
||||||
|
|
||||||
intent.setData(Uri.parse(getString(R.string.AccountLockedFragment__learn_more_url)));
|
private static int getLockoutDays(long timeRemainingMs) {
|
||||||
|
return (int) TimeUnit.MILLISECONDS.toDays(timeRemainingMs) + 1;
|
||||||
startActivity(intent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onNext() {
|
private void onNext() {
|
||||||
|
@ -171,11 +171,11 @@ public final class EnterCodeFragment extends BaseRegistrationFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onKbsAccountLocked(long timeRemaining) {
|
public void onKbsAccountLocked(@Nullable Long timeRemaining) {
|
||||||
|
if (timeRemaining != null) {
|
||||||
model.setTimeRemaining(timeRemaining);
|
model.setTimeRemaining(timeRemaining);
|
||||||
RegistrationLockFragmentDirections.ActionAccountLocked action = RegistrationLockFragmentDirections.actionAccountLocked(timeRemaining);
|
}
|
||||||
|
Navigation.findNavController(requireView()).navigate(RegistrationLockFragmentDirections.actionAccountLocked());
|
||||||
Navigation.findNavController(requireView()).navigate(action);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -192,7 +192,7 @@ public final class RegistrationLockFragment extends BaseRegistrationFragment {
|
|||||||
|
|
||||||
if (triesRemaining == 0) {
|
if (triesRemaining == 0) {
|
||||||
Log.w(TAG, "Account locked. User out of attempts on KBS.");
|
Log.w(TAG, "Account locked. User out of attempts on KBS.");
|
||||||
lockAccount(timeRemaining);
|
onAccountLocked();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,10 +226,12 @@ public final class RegistrationLockFragment extends BaseRegistrationFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onKbsAccountLocked(long timeRemaining) {
|
public void onKbsAccountLocked(@Nullable Long timeRemaining) {
|
||||||
getModel().setTimeRemaining(timeRemaining);
|
if (timeRemaining != null) {
|
||||||
|
model.setTimeRemaining(timeRemaining);
|
||||||
|
}
|
||||||
|
|
||||||
lockAccount(timeRemaining);
|
onAccountLocked();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -254,10 +256,8 @@ public final class RegistrationLockFragment extends BaseRegistrationFragment {
|
|||||||
return (int) TimeUnit.MILLISECONDS.toDays(timeRemainingMs) + 1;
|
return (int) TimeUnit.MILLISECONDS.toDays(timeRemainingMs) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void lockAccount(long timeRemaining) {
|
private void onAccountLocked() {
|
||||||
RegistrationLockFragmentDirections.ActionAccountLocked action = RegistrationLockFragmentDirections.actionAccountLocked(timeRemaining);
|
Navigation.findNavController(requireView()).navigate(RegistrationLockFragmentDirections.actionAccountLocked());
|
||||||
|
|
||||||
Navigation.findNavController(requireView()).navigate(action);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateKeyboard(@NonNull PinKeyboardType keyboard) {
|
private void updateKeyboard(@NonNull PinKeyboardType keyboard) {
|
||||||
|
@ -35,6 +35,7 @@ import org.whispersystems.libsignal.util.KeyHelper;
|
|||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.KeyBackupService;
|
import org.whispersystems.signalservice.api.KeyBackupService;
|
||||||
import org.whispersystems.signalservice.api.KeyBackupServicePinException;
|
import org.whispersystems.signalservice.api.KeyBackupServicePinException;
|
||||||
|
import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException;
|
||||||
import org.whispersystems.signalservice.api.RegistrationLockData;
|
import org.whispersystems.signalservice.api.RegistrationLockData;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||||
import org.whispersystems.signalservice.api.kbs.HashedPin;
|
import org.whispersystems.signalservice.api.kbs.HashedPin;
|
||||||
@ -94,6 +95,9 @@ public final class CodeVerificationRequest {
|
|||||||
kbsToken = kbsTokenResponse;
|
kbsToken = kbsTokenResponse;
|
||||||
verifyAccount(context, credentials, code, pin, kbsTokenResponse, basicStorageCredentials, fcmToken);
|
verifyAccount(context, credentials, code, pin, kbsTokenResponse, basicStorageCredentials, fcmToken);
|
||||||
return Result.SUCCESS;
|
return Result.SUCCESS;
|
||||||
|
} catch (KeyBackupSystemNoDataException e) {
|
||||||
|
Log.w(TAG, "No data found on KBS");
|
||||||
|
return Result.KBS_ACCOUNT_LOCKED;
|
||||||
} catch (KeyBackupSystemWrongPinException e) {
|
} catch (KeyBackupSystemWrongPinException e) {
|
||||||
kbsToken = e.getTokenResponse();
|
kbsToken = e.getTokenResponse();
|
||||||
return Result.KBS_WRONG_PIN;
|
return Result.KBS_WRONG_PIN;
|
||||||
@ -156,7 +160,7 @@ public final class CodeVerificationRequest {
|
|||||||
break;
|
break;
|
||||||
case KBS_ACCOUNT_LOCKED:
|
case KBS_ACCOUNT_LOCKED:
|
||||||
Log.w(TAG, "KBS Account is locked");
|
Log.w(TAG, "KBS Account is locked");
|
||||||
callback.onKbsAccountLocked(lockedException.getTimeRemaining());
|
callback.onKbsAccountLocked(lockedException != null ? lockedException.getTimeRemaining() : null);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -184,7 +188,7 @@ public final class CodeVerificationRequest {
|
|||||||
@Nullable TokenResponse kbsTokenResponse,
|
@Nullable TokenResponse kbsTokenResponse,
|
||||||
@Nullable String kbsStorageCredentials,
|
@Nullable String kbsStorageCredentials,
|
||||||
@Nullable String fcmToken)
|
@Nullable String fcmToken)
|
||||||
throws IOException, KeyBackupSystemWrongPinException
|
throws IOException, KeyBackupSystemWrongPinException, KeyBackupSystemNoDataException
|
||||||
{
|
{
|
||||||
boolean isV2KbsPin = kbsTokenResponse != null;
|
boolean isV2KbsPin = kbsTokenResponse != null;
|
||||||
int registrationId = KeyHelper.generateRegistrationId(false);
|
int registrationId = KeyHelper.generateRegistrationId(false);
|
||||||
@ -284,7 +288,7 @@ public final class CodeVerificationRequest {
|
|||||||
private static @Nullable RegistrationLockData restoreMasterKey(@Nullable String pin,
|
private static @Nullable RegistrationLockData restoreMasterKey(@Nullable String pin,
|
||||||
@Nullable String basicStorageCredentials,
|
@Nullable String basicStorageCredentials,
|
||||||
@NonNull TokenResponse tokenResponse)
|
@NonNull TokenResponse tokenResponse)
|
||||||
throws IOException, KeyBackupSystemWrongPinException
|
throws IOException, KeyBackupSystemWrongPinException, KeyBackupSystemNoDataException
|
||||||
{
|
{
|
||||||
if (pin == null) return null;
|
if (pin == null) return null;
|
||||||
|
|
||||||
@ -304,7 +308,7 @@ public final class CodeVerificationRequest {
|
|||||||
if (kbsData != null) {
|
if (kbsData != null) {
|
||||||
Log.i(TAG, "Found registration lock token on KBS.");
|
Log.i(TAG, "Found registration lock token on KBS.");
|
||||||
} else {
|
} else {
|
||||||
Log.i(TAG, "No KBS data found.");
|
throw new AssertionError("Null not expected");
|
||||||
}
|
}
|
||||||
return kbsData;
|
return kbsData;
|
||||||
} catch (UnauthenticatedResponseException e) {
|
} catch (UnauthenticatedResponseException e) {
|
||||||
@ -340,11 +344,11 @@ public final class CodeVerificationRequest {
|
|||||||
void onIncorrectKbsRegistrationLockPin(@NonNull TokenResponse kbsTokenResponse);
|
void onIncorrectKbsRegistrationLockPin(@NonNull TokenResponse kbsTokenResponse);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* V2 (KBS) pin is set, but there is no data on KBS
|
* V2 (KBS) pin is set, but there is no data on KBS.
|
||||||
*
|
*
|
||||||
* @param timeRemaining Time until pin expires and number can be reused.
|
* @param timeRemaining Non-null if known.
|
||||||
*/
|
*/
|
||||||
void onKbsAccountLocked(long timeRemaining);
|
void onKbsAccountLocked(@Nullable Long timeRemaining);
|
||||||
|
|
||||||
void onRateLimited();
|
void onRateLimited();
|
||||||
|
|
||||||
|
@ -157,12 +157,7 @@
|
|||||||
android:id="@+id/accountLockedFragment"
|
android:id="@+id/accountLockedFragment"
|
||||||
android:name="org.thoughtcrime.securesms.registration.fragments.AccountLockedFragment"
|
android:name="org.thoughtcrime.securesms.registration.fragments.AccountLockedFragment"
|
||||||
android:label="fragment_account_locked"
|
android:label="fragment_account_locked"
|
||||||
tools:layout="@layout/account_locked_fragment">
|
tools:layout="@layout/account_locked_fragment"/>
|
||||||
|
|
||||||
<argument
|
|
||||||
android:name="timeRemaining"
|
|
||||||
app:argType="long" />
|
|
||||||
</fragment>
|
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/captchaFragment"
|
android:id="@+id/captchaFragment"
|
||||||
|
@ -124,7 +124,7 @@ public final class KeyBackupService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RegistrationLockData restorePin(HashedPin hashedPin)
|
public RegistrationLockData restorePin(HashedPin hashedPin)
|
||||||
throws UnauthenticatedResponseException, IOException, KeyBackupServicePinException
|
throws UnauthenticatedResponseException, IOException, KeyBackupServicePinException, KeyBackupSystemNoDataException
|
||||||
{
|
{
|
||||||
int attempt = 0;
|
int attempt = 0;
|
||||||
SecureRandom random = new SecureRandom();
|
SecureRandom random = new SecureRandom();
|
||||||
@ -157,7 +157,7 @@ public final class KeyBackupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private RegistrationLockData restorePin(HashedPin hashedPin, TokenResponse token)
|
private RegistrationLockData restorePin(HashedPin hashedPin, TokenResponse token)
|
||||||
throws UnauthenticatedResponseException, IOException, TokenException
|
throws UnauthenticatedResponseException, IOException, TokenException, KeyBackupSystemNoDataException
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
final int remainingTries = token.getTries();
|
final int remainingTries = token.getTries();
|
||||||
@ -190,14 +190,15 @@ public final class KeyBackupService {
|
|||||||
throw new TokenException(nextToken, canRetry);
|
throw new TokenException(nextToken, canRetry);
|
||||||
case MISSING:
|
case MISSING:
|
||||||
Log.i(TAG, "Restore OK! No data though");
|
Log.i(TAG, "Restore OK! No data though");
|
||||||
return null;
|
throw new KeyBackupSystemNoDataException();
|
||||||
case NOT_YET_VALID:
|
case NOT_YET_VALID:
|
||||||
throw new UnauthenticatedResponseException("Key is not valid yet, clock mismatch");
|
throw new UnauthenticatedResponseException("Key is not valid yet, clock mismatch");
|
||||||
|
default:
|
||||||
|
throw new AssertionError("Unexpected case");
|
||||||
}
|
}
|
||||||
} catch (InvalidCiphertextException e) {
|
} catch (InvalidCiphertextException e) {
|
||||||
throw new UnauthenticatedResponseException(e);
|
throw new UnauthenticatedResponseException(e);
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private RemoteAttestation getAndVerifyRemoteAttestation() throws UnauthenticatedResponseException, IOException {
|
private RemoteAttestation getAndVerifyRemoteAttestation() throws UnauthenticatedResponseException, IOException {
|
||||||
@ -277,7 +278,7 @@ public final class KeyBackupService {
|
|||||||
public interface RestoreSession extends HashSession {
|
public interface RestoreSession extends HashSession {
|
||||||
|
|
||||||
RegistrationLockData restorePin(HashedPin hashedPin)
|
RegistrationLockData restorePin(HashedPin hashedPin)
|
||||||
throws UnauthenticatedResponseException, IOException, KeyBackupServicePinException;
|
throws UnauthenticatedResponseException, IOException, KeyBackupServicePinException, KeyBackupSystemNoDataException;
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface PinChangeSession extends HashSession {
|
public interface PinChangeSession extends HashSession {
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
package org.whispersystems.signalservice.api;
|
||||||
|
|
||||||
|
public final class KeyBackupSystemNoDataException extends Exception {
|
||||||
|
|
||||||
|
KeyBackupSystemNoDataException() {
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user