Skip to content

Commit

Permalink
Re-enable and clean up Signal PINs.
Browse files Browse the repository at this point in the history
- Require PINs during registration agian.
- Change min length to 4.
- Allow the full-screen megaphone to be enabled remotely.
- Clean up and remove some code.
  • Loading branch information
greyson-signal committed Apr 3, 2020
1 parent 6f961ad commit f1ea035
Show file tree
Hide file tree
Showing 16 changed files with 67 additions and 186 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package org.thoughtcrime.securesms.keyvalue;

import androidx.annotation.CheckResult;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import org.thoughtcrime.securesms.lock.v2.PinKeyboardType;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.whispersystems.signalservice.api.RegistrationLockData;
import org.whispersystems.signalservice.api.kbs.MasterKey;
Expand All @@ -19,7 +17,6 @@ public final class KbsValues {
private static final String MASTER_KEY = "kbs.registration_lock_master_key";
private static final String TOKEN_RESPONSE = "kbs.token_response";
private static final String LOCK_LOCAL_PIN_HASH = "kbs.registration_lock_local_pin_hash";
private static final String KEYBOARD_TYPE = "kbs.keyboard_type";

private final KeyValueStore store;

Expand All @@ -35,7 +32,6 @@ public void clearRegistrationLock() {
.remove(V2_LOCK_ENABLED)
.remove(TOKEN_RESPONSE)
.remove(LOCK_LOCAL_PIN_HASH)
.remove(KEYBOARD_TYPE)
.commit();
}

Expand Down Expand Up @@ -97,15 +93,15 @@ public synchronized void setRegistrationLockMasterKey(@NonNull RegistrationLockD
}
}

public @Nullable String getLocalPinHash() {
public synchronized @Nullable String getLocalPinHash() {
return store.getString(LOCK_LOCAL_PIN_HASH, null);
}

public boolean isV2RegistrationLockEnabled() {
public synchronized boolean isV2RegistrationLockEnabled() {
return store.getBoolean(V2_LOCK_ENABLED, false);
}

public @Nullable TokenResponse getRegistrationLockTokenResponse() {
public synchronized @Nullable TokenResponse getRegistrationLockTokenResponse() {
String token = store.getString(TOKEN_RESPONSE, null);

if (token == null) return null;
Expand All @@ -116,19 +112,4 @@ public boolean isV2RegistrationLockEnabled() {
throw new AssertionError(e);
}
}

public void setKeyboardType(@NonNull PinKeyboardType keyboardType) {
store.beginWrite()
.putString(KEYBOARD_TYPE, keyboardType.getCode())
.commit();
}

@CheckResult
public @NonNull PinKeyboardType getKeyboardType() {
return PinKeyboardType.fromCode(store.getString(KEYBOARD_TYPE, null));
}

public boolean hasMigratedToPinsForAll() {
return store.getString(KEYBOARD_TYPE, null) != null && store.getBoolean(V2_LOCK_ENABLED, false);
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
package org.thoughtcrime.securesms.keyvalue;

import androidx.annotation.CheckResult;
import androidx.annotation.NonNull;

import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.lock.SignalPinReminders;
import org.thoughtcrime.securesms.lock.v2.PinKeyboardType;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.TextSecurePreferences;

/**
* Specifically handles just the UI/UX state around PINs. For actual keys, see {@link KbsValues}.
*/
public final class PinValues {

private static final String TAG = Log.tag(PinValues.class);

private static final String LAST_SUCCESSFUL_ENTRY = "pin.last_successful_entry";
private static final String NEXT_INTERVAL = "pin.interval_index";
private static final String KEYBOARD_TYPE = "kbs.keyboard_type";

private final KeyValueStore store;

Expand Down Expand Up @@ -47,7 +55,6 @@ public void onEntrySkipWithWrongGuess() {
.apply();
}


public void onPinChange() {
long nextInterval = SignalPinReminders.INITIAL_INTERVAL;
Log.i(TAG, "onPinChange() nextInterval: " + nextInterval);
Expand All @@ -65,4 +72,14 @@ public long getCurrentInterval() {
public long getLastSuccessfulEntryTime() {
return store.getLong(LAST_SUCCESSFUL_ENTRY, TextSecurePreferences.getRegistrationLockLastReminderTime(ApplicationDependencies.getApplication()));
}

public void setKeyboardType(@NonNull PinKeyboardType keyboardType) {
store.beginWrite()
.putString(KEYBOARD_TYPE, keyboardType.getCode())
.commit();
}

public @NonNull PinKeyboardType getKeyboardType() {
return PinKeyboardType.fromCode(store.getString(KEYBOARD_TYPE, null));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ public final class RegistrationValues {
public synchronized void onFirstEverAppLaunch() {
store.beginWrite()
.putBoolean(REGISTRATION_COMPLETE, false)
// TODO [greyson] [pins] Maybe re-enable in the future
// .putBoolean(PIN_REQUIRED, true)
.putBoolean(PIN_REQUIRED, true)
.commit();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ private static TextWatcher getV2PinWatcher(@NonNull Context context, AlertDialog
if (s == null) return;
String pin = s.toString();
if (TextUtils.isEmpty(pin)) return;
if (pin.length() < KbsConstants.LEGACY_MINIMUM_PIN_LENGTH) return;
if (pin.length() < KbsConstants.MINIMUM_PIN_LENGTH) return;

if (PinHashing.verifyLocalPinHash(localPinHash, pin)) {
dialog.dismiss();
Expand Down Expand Up @@ -186,9 +186,9 @@ public static void showRegistrationLockPrompt(@NonNull Context context, @NonNull
String pinValue = pin.getText().toString().replace(" ", "");
String repeatValue = repeat.getText().toString().replace(" ", "");

if (pinValue.length() < KbsConstants.LEGACY_MINIMUM_PIN_LENGTH) {
if (pinValue.length() < KbsConstants.MINIMUM_PIN_LENGTH) {
Toast.makeText(context,
context.getString(R.string.RegistrationLockDialog_the_registration_lock_pin_must_be_at_least_d_digits, KbsConstants.LEGACY_MINIMUM_PIN_LENGTH),
context.getString(R.string.RegistrationLockDialog_the_registration_lock_pin_must_be_at_least_d_digits, KbsConstants.MINIMUM_PIN_LENGTH),
Toast.LENGTH_LONG).show();
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public static void show(@NonNull Context context, @NonNull Launcher launcher, @N
}
});

switch (SignalStore.kbsValues().getKeyboardType()) {
switch (SignalStore.pinValues().getKeyboardType()) {
case NUMERIC:
pinEditText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
break;
Expand Down Expand Up @@ -115,7 +115,7 @@ public void onClick(@NonNull View widget) {
pinEditText.addTextChangedListener(new SimpleTextWatcher() {
@Override
public void onTextChanged(String text) {
if (text.length() >= KbsConstants.minimumPossiblePinLength()) {
if (text.length() >= KbsConstants.MINIMUM_PIN_LENGTH) {
submit.setEnabled(true);
} else {
submit.setEnabled(false);
Expand Down Expand Up @@ -192,7 +192,7 @@ public void verifyPin(@Nullable String pin, @NonNull Callback callback) {
if (pin == null) return;
if (TextUtils.isEmpty(pin)) return;

if (pin.length() < KbsConstants.minimumPossiblePinLength()) return;
if (pin.length() < KbsConstants.MINIMUM_PIN_LENGTH) return;

if (PinHashing.verifyLocalPinHash(localPinHash, pin)) {
callback.onPinCorrect();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ void setPin(@NonNull KbsPin kbsPin, @NonNull PinKeyboardType keyboard, @NonNull
TextSecurePreferences.clearOldRegistrationLockPin(context);
TextSecurePreferences.setRegistrationLockLastReminderTime(context, System.currentTimeMillis());
TextSecurePreferences.setRegistrationLockNextReminderInterval(context, RegistrationLockReminders.INITIAL_INTERVAL);
SignalStore.kbsValues().setKeyboardType(keyboard);
SignalStore.pinValues().setKeyboardType(keyboard);
ApplicationDependencies.getMegaphoneRepository().markFinished(Megaphones.Event.PINS_FOR_ALL);

Log.i(TAG, "Pin set on KBS");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@

public final class KbsConstants {

public static final int MINIMUM_PIN_LENGTH = 6;
public static final int LEGACY_MINIMUM_PIN_LENGTH = 4;
public static final int MINIMUM_PIN_LENGTH = 4;

private KbsConstants() { }

public static int minimumPossiblePinLength() {
return LEGACY_MINIMUM_PIN_LENGTH;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,4 @@ private PinUtil() {}
public static boolean userHasPin(@NonNull Context context) {
return TextSecurePreferences.isV1RegistrationLockEnabled(context) || SignalStore.kbsValues().isV2RegistrationLockEnabled();
}

public static boolean shouldShowPinCreationDuringRegistration(@NonNull Context context) {
return FeatureFlags.pinsForAll() && !PinUtil.userHasPin(context);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -136,38 +136,18 @@ private static Map<Event, MegaphoneSchedule> buildDisplayOrder() {
})
.build();
} else {
Megaphone.Builder builder = new Megaphone.Builder(Event.PINS_FOR_ALL, Megaphone.Style.BASIC)
.setMandatory(true)
.setImage(R.drawable.kbs_pin_megaphone);

if (PinUtil.userHasPin(ApplicationDependencies.getApplication())) {
return buildPinsForAllMegaphoneForUserWithPin(builder.enableSnooze(null));
} else {
return buildPinsForAllMegaphoneForUserWithoutPin(builder.enableSnooze(null));
}
}
}

private static @NonNull Megaphone buildPinsForAllMegaphoneForUserWithPin(@NonNull Megaphone.Builder builder) {
return builder.setTitle(R.string.KbsMegaphone__introducing_pins)
.setBody(R.string.KbsMegaphone__your_registration_lock_is_now_called_a_pin)
.setActionButton(R.string.KbsMegaphone__update_pin, (megaphone, listener) -> {
Intent intent = CreateKbsPinActivity.getIntentForPinChangeFromSettings(ApplicationDependencies.getApplication());

listener.onMegaphoneNavigationRequested(intent, CreateKbsPinActivity.REQUEST_NEW_PIN);
})
.build();
}

private static @NonNull Megaphone buildPinsForAllMegaphoneForUserWithoutPin(@NonNull Megaphone.Builder builder) {
return builder.setTitle(R.string.KbsMegaphone__create_a_pin)
.setBody(R.string.KbsMegaphone__pins_add_another_layer_of_security_to_your_signal_account)
.setActionButton(R.string.KbsMegaphone__create_pin, (megaphone, listener) -> {
Intent intent = CreateKbsPinActivity.getIntentForPinCreate(ApplicationDependencies.getApplication());
return new Megaphone.Builder(Event.PINS_FOR_ALL, Megaphone.Style.BASIC)
.setMandatory(true)
.setImage(R.drawable.kbs_pin_megaphone)
.setTitle(R.string.KbsMegaphone__create_a_pin)
.setBody(R.string.KbsMegaphone__pins_add_another_layer_of_security_to_your_signal_account)
.setActionButton(R.string.KbsMegaphone__create_pin, (megaphone, listener) -> {
Intent intent = CreateKbsPinActivity.getIntentForPinCreate(ApplicationDependencies.getApplication());

listener.onMegaphoneNavigationRequested(intent, CreateKbsPinActivity.REQUEST_NEW_PIN);
})
.build();
listener.onMegaphoneNavigationRequested(intent, CreateKbsPinActivity.REQUEST_NEW_PIN);
})
.build();
}
}

private static @NonNull Megaphone buildPinReminderMegaphone(@NonNull Context context) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
package org.thoughtcrime.securesms.megaphone;

import android.content.Context;

import androidx.annotation.VisibleForTesting;

import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
import org.thoughtcrime.securesms.util.CensorshipUtil;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;

import java.util.concurrent.TimeUnit;

Expand All @@ -19,36 +14,25 @@ class PinsForAllSchedule implements MegaphoneSchedule {
@VisibleForTesting
static final long DAYS_UNTIL_FULLSCREEN = 8L;

@VisibleForTesting
static final long DAYS_REMAINING_MAX = DAYS_UNTIL_FULLSCREEN - 1;

private final MegaphoneSchedule schedule = new RecurringSchedule(TimeUnit.DAYS.toMillis(2));

static boolean shouldDisplayFullScreen(long firstVisible, long currentTime) {
return false;
// TODO [greyson] [pins] Maybe re-enable if we ever do a blocking flow again
// if (pinCreationFailedDuringRegistration()) {
// return true;
// }
//
// if (firstVisible == 0L) {
// return false;
// } else {
// return currentTime - firstVisible >= TimeUnit.DAYS.toMillis(DAYS_UNTIL_FULLSCREEN);
// }
}
if (!FeatureFlags.pinsForAllMandatory()) {
return false;
}

static long getDaysRemaining(long firstVisible, long currentTime) {
if (firstVisible == 0L) {
return DAYS_REMAINING_MAX;
} else {
return Util.clamp(DAYS_REMAINING_MAX - TimeUnit.MILLISECONDS.toDays(currentTime - firstVisible), 0, DAYS_REMAINING_MAX);
return false;
}

return currentTime - firstVisible >= TimeUnit.DAYS.toMillis(DAYS_UNTIL_FULLSCREEN);
}

@Override
public boolean shouldDisplay(int seenCount, long lastSeen, long firstVisible, long currentTime) {
if (!isEnabled()) return false;
if (!isEnabled()) {
return false;
}

if (shouldDisplayFullScreen(firstVisible, currentTime)) {
return true;
Expand All @@ -58,6 +42,10 @@ public boolean shouldDisplay(int seenCount, long lastSeen, long firstVisible, lo
}

private static boolean isEnabled() {
if (SignalStore.kbsValues().isV2RegistrationLockEnabled()) {
return false;
}

if (FeatureFlags.pinsForAllMegaphoneKillSwitch()) {
return false;
}
Expand All @@ -74,10 +62,6 @@ private static boolean isEnabled() {
return false;
}

if (SignalStore.kbsValues().hasMigratedToPinsForAll()) {
return false;
}

return FeatureFlags.pinsForAll();
}

Expand All @@ -87,8 +71,7 @@ private static boolean pinCreationFailedDuringRegistration() {
!TextSecurePreferences.isV1RegistrationLockEnabled(ApplicationDependencies.getApplication());
}

private static final boolean newlyRegisteredV1PinUser() {
private static boolean newlyRegisteredV1PinUser() {
return SignalStore.registrationValues().pinWasRequiredAtRegistration() && TextSecurePreferences.isV1RegistrationLockEnabled(ApplicationDependencies.getApplication());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -307,10 +307,6 @@ private void startAvatarSelection() {
private void handleUpload() {
viewModel.submitProfile(uploadResult -> {
if (uploadResult == EditProfileRepository.UploadResult.SUCCESS) {
if (!PinUtil.shouldShowPinCreationDuringRegistration(requireContext())) {
SignalStore.registrationValues().setRegistrationComplete();
}

ApplicationDependencies.getMegaphoneRepository().markFinished(Megaphones.Event.PROFILE_NAMES_FOR_ALL);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) handleFinishedLollipop();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,8 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat

profile.putExtra(EditProfileActivity.SHOW_TOOLBAR, false);

if (PinUtil.shouldShowPinCreationDuringRegistration(requireContext())) {
Intent kbs = CreateKbsPinActivity.getIntentForPinCreate(requireContext());
activity.startActivity(chainIntents(chainIntents(profile, kbs), main));
} else {
activity.startActivity(chainIntents(profile, main));
}
Intent kbs = CreateKbsPinActivity.getIntentForPinCreate(requireContext());
activity.startActivity(chainIntents(chainIntents(profile, kbs), main));
}

activity.finish();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ private void enableAndFocusPinEntry() {
}

private void handleSuccessfulPinEntry() {
SignalStore.kbsValues().setKeyboardType(getPinEntryKeyboardType());
SignalStore.pinValues().setKeyboardType(getPinEntryKeyboardType());

if (FeatureFlags.storageServiceRestore()) {
long startTime = System.currentTimeMillis();
Expand Down

0 comments on commit f1ea035

Please sign in to comment.