Update pin opt-out strings and behavior.

This commit is contained in:
Greyson Parrelli
2020-07-10 18:06:51 -04:00
parent e2021231c6
commit fce3df0c82
8 changed files with 60 additions and 76 deletions

View File

@@ -164,7 +164,7 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
}
private boolean userMustCreateSignalPin() {
return !SignalStore.registrationValues().isRegistrationComplete() && !SignalStore.kbsValues().hasPin() && !SignalStore.kbsValues().lastPinCreateFailed();
return !SignalStore.registrationValues().isRegistrationComplete() && !SignalStore.kbsValues().hasPin() && !SignalStore.kbsValues().lastPinCreateFailed() && !SignalStore.kbsValues().hasOptedOut();
}
private boolean userMustSetProfileName() {

View File

@@ -62,6 +62,7 @@ public final class KbsValues extends SignalStoreValues {
.putString(LOCK_LOCAL_PIN_HASH, PinHashing.localPinHash(pin))
.putString(PIN, pin)
.putLong(LAST_CREATE_FAILED_TIMESTAMP, -1)
.putBoolean(OPTED_OUT, false)
.commit();
}
@@ -144,27 +145,16 @@ public final class KbsValues extends SignalStoreValues {
return getLocalPinHash() != null;
}
/**
* Should only be called by {@link org.thoughtcrime.securesms.pin.PinState}.
*/
public synchronized void optIn() {
putBoolean(OPTED_OUT, false);
}
/**
* Should only be called by {@link org.thoughtcrime.securesms.pin.PinState}.
*/
/** Should only be called by {@link org.thoughtcrime.securesms.pin.PinState}. */
public synchronized void optOut() {
putBoolean(OPTED_OUT, true);
}
/**
* Should only be called by {@link org.thoughtcrime.securesms.pin.PinState}.
*/
public synchronized void resetMasterKey() {
getStore().beginWrite()
.remove(MASTER_KEY)
.apply();
.putBoolean(OPTED_OUT, true)
.remove(TOKEN_RESPONSE)
.putBlob(MASTER_KEY, MasterKey.createNew(new SecureRandom()).serialize())
.remove(LOCK_LOCAL_PIN_HASH)
.remove(PIN)
.putLong(LAST_CREATE_FAILED_TIMESTAMP, -1)
.commit();
}
public synchronized boolean hasOptedOut() {

View File

@@ -100,7 +100,8 @@ abstract class BaseKbsPinFragment<ViewModel extends BaseKbsPinViewModel> extends
@Override
public void onPrepareOptionsMenu(@NonNull Menu menu) {
if (RegistrationLockUtil.userHasRegistrationLock(requireContext()) ||
SignalStore.kbsValues().hasPin())
SignalStore.kbsValues().hasPin() ||
SignalStore.kbsValues().hasOptedOut())
{
menu.clear();
}

View File

@@ -22,6 +22,7 @@ public class LogSectionPin implements LogSection {
.append("ReglockV1: ").append(TextSecurePreferences.isV1RegistrationLockEnabled(context)).append("\n")
.append("ReglockV2: ").append(SignalStore.kbsValues().isV2RegistrationLockEnabled()).append("\n")
.append("Signal PIN: ").append(SignalStore.kbsValues().hasPin()).append("\n")
.append("Opted Out: ").append(SignalStore.kbsValues().hasOptedOut()).append("\n")
.append("Last Creation Failed: ").append(SignalStore.kbsValues().lastPinCreateFailed()).append("\n")
.append("Needs Account Restore: ").append(SignalStore.storageServiceValues().needsAccountRestore()).append("\n")
.append("PIN Required at Registration: ").append(SignalStore.registrationValues().pinWasRequiredAtRegistration()).append("\n")

View File

@@ -34,30 +34,25 @@ public final class PinOptOutDialog {
}
private static void show(@NonNull Context context,
boolean skip,
boolean isForSkip,
@NonNull Runnable onSuccess,
@NonNull Runnable onFailed)
{
AlertDialog dialog = new AlertDialog.Builder(context)
.setTitle(R.string.PinOptOutDialog_warning)
.setMessage(R.string.PinOptOutDialog_disabling_pins_will_create_a_hidden_high_entropy_pin)
.setMessage(R.string.PinOptOutDialog_if_you_disable_the_pin_you_will_lose_all_data)
.setCancelable(true)
.setPositiveButton(R.string.PinOptOutDialog_disable_pin, (d, which) -> {
d.dismiss();
AlertDialog progress = SimpleProgressDialog.show(context);
SimpleTask.run(() -> {
try {
if (skip) {
PinState.onPinCreationSkipped(context);
} else {
PinState.onPinOptOut(context);
}
return true;
} catch (IOException | UnauthenticatedResponseException e) {
Log.w(TAG, e);
return false;
if (isForSkip) {
PinState.onPinCreationSkipped();
} else {
PinState.onPinOptOut();
}
return true;
}, success -> {
if (success) {
onSuccess.run();

View File

@@ -18,9 +18,7 @@ import org.thoughtcrime.securesms.lock.v2.PinKeyboardType;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.megaphone.Megaphones;
import org.thoughtcrime.securesms.registration.service.KeyBackupSystemWrongPinException;
import org.thoughtcrime.securesms.util.Hex;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.KbsPinData;
import org.whispersystems.signalservice.api.KeyBackupService;
@@ -32,6 +30,7 @@ import org.whispersystems.signalservice.internal.contacts.crypto.Unauthenticated
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
@@ -156,10 +155,19 @@ public final class PinState {
{
Log.i(TAG, "onPinChangedOrCreated()");
boolean isFirstPin = !SignalStore.kbsValues().hasPin() || SignalStore.kbsValues().hasOptedOut();
KbsValues kbsValues = SignalStore.kbsValues();
boolean isFirstPin = !kbsValues.hasPin() || kbsValues.hasOptedOut();
MasterKey masterKey = kbsValues.getOrCreateMasterKey();
KeyBackupService keyBackupService = ApplicationDependencies.getKeyBackupService();
KeyBackupService.PinChangeSession pinChangeSession = keyBackupService.newPinChangeSession();
HashedPin hashedPin = PinHashing.hashPin(pin, pinChangeSession);
KbsPinData kbsData = pinChangeSession.setPin(hashedPin, masterKey);
setPin(context, pin, keyboard);
SignalStore.kbsValues().optIn();
kbsValues.setKbsMasterKey(kbsData, pin);
TextSecurePreferences.clearRegistrationLockV1(context);
SignalStore.pinValues().setKeyboardType(keyboard);
SignalStore.pinValues().resetPinReminders();
ApplicationDependencies.getMegaphoneRepository().markFinished(Megaphones.Event.PINS_FOR_ALL);
if (isFirstPin) {
Log.i(TAG, "First time setting a PIN. Refreshing attributes to set the 'storage' capability.");
@@ -185,13 +193,11 @@ public final class PinState {
* Invoked when the user has enabled the "PIN opt out" setting.
*/
@WorkerThread
public static synchronized void onPinOptOut(@NonNull Context context)
throws IOException, UnauthenticatedResponseException
{
public static synchronized void onPinOptOut() {
Log.i(TAG, "onPinOptOutEnabled()");
assertState(State.PIN_WITH_REGISTRATION_LOCK_DISABLED, State.NO_REGISTRATION_LOCK);
optOutOfPin(context);
optOutOfPin();
updateState(buildInferredStateFromOtherFields());
}
@@ -200,13 +206,11 @@ public final class PinState {
* Invoked when the user has chosen to skip PIN creation.
*/
@WorkerThread
public static synchronized void onPinCreationSkipped(@NonNull Context context)
throws IOException, UnauthenticatedResponseException
{
public static synchronized void onPinCreationSkipped() {
Log.i(TAG, "onPinCreationSkipped()");
assertState(State.NO_REGISTRATION_LOCK);
optOutOfPin(context);
optOutOfPin();
updateState(buildInferredStateFromOtherFields());
}
@@ -295,6 +299,20 @@ public final class PinState {
}
}
@WorkerThread
private static void bestEffortForcePushStorage() {
Optional<JobTracker.JobState> result = ApplicationDependencies.getJobManager().runSynchronously(new StorageForcePushJob(), TimeUnit.SECONDS.toMillis(10));
if (result.isPresent() && result.get() == JobTracker.JobState.SUCCESS) {
Log.i(TAG, "Storage was force-pushed successfully.");
} else if (result.isPresent()) {
Log.w(TAG, "Storage force-pushed finished, but was not successful. Enqueuing one for later. (Result: " + result.get() + ")");
ApplicationDependencies.getJobManager().add(new RefreshAttributesJob());
} else {
Log.w(TAG, "Storage fore push did not finish in the allotted time. It'll finish later.");
}
}
@WorkerThread
private static void resetPinRetryCount(@NonNull Context context, @Nullable String pin, @NonNull KbsPinData kbsData) {
if (pin == null) {
@@ -322,34 +340,13 @@ public final class PinState {
}
@WorkerThread
private static void setPin(@NonNull Context context, @NonNull String pin, @NonNull PinKeyboardType keyboard)
throws IOException, UnauthenticatedResponseException
{
KbsValues kbsValues = SignalStore.kbsValues();
MasterKey masterKey = kbsValues.getOrCreateMasterKey();
KeyBackupService keyBackupService = ApplicationDependencies.getKeyBackupService();
KeyBackupService.PinChangeSession pinChangeSession = keyBackupService.newPinChangeSession();
HashedPin hashedPin = PinHashing.hashPin(pin, pinChangeSession);
KbsPinData kbsData = pinChangeSession.setPin(hashedPin, masterKey);
kbsValues.setKbsMasterKey(kbsData, pin);
TextSecurePreferences.clearRegistrationLockV1(context);
SignalStore.pinValues().setKeyboardType(keyboard);
SignalStore.pinValues().resetPinReminders();
ApplicationDependencies.getMegaphoneRepository().markFinished(Megaphones.Event.PINS_FOR_ALL);
}
@WorkerThread
private static void optOutOfPin(@NonNull Context context)
throws IOException, UnauthenticatedResponseException
{
SignalStore.kbsValues().resetMasterKey();
setPin(context, Hex.toStringCondensed(Util.getSecretBytes(32)), PinKeyboardType.ALPHA_NUMERIC);
private static void optOutOfPin() {
SignalStore.kbsValues().optOut();
ApplicationDependencies.getJobManager().add(new StorageForcePushJob());
ApplicationDependencies.getMegaphoneRepository().markFinished(Megaphones.Event.PINS_FOR_ALL);
bestEffortRefreshAttributes();
bestEffortForcePushStorage();
}
private static @NonNull State assertState(State... allowed) {