From fea2b6253f6aeabdfb0e2fdc7c65495d90ace9cf Mon Sep 17 00:00:00 2001 From: Alan Evans Date: Fri, 24 Jan 2020 18:38:48 -0500 Subject: [PATCH] KBS remote feature flag. --- .../lock/RegistrationLockDialog.java | 59 +++++++++++-------- .../RegistrationPinV2MigrationJob.java | 6 ++ .../service/CodeVerificationRequest.java | 7 ++- .../securesms/util/FeatureFlags.java | 25 +++++--- .../securesms/util/TextSecurePreferences.java | 17 +++--- 5 files changed, 74 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockDialog.java b/app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockDialog.java index 40e1547af..d96a33646 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockDialog.java +++ b/app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockDialog.java @@ -35,6 +35,7 @@ import org.thoughtcrime.securesms.keyvalue.KbsValues; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.migrations.RegistrationPinV2MigrationJob; +import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.ThemeUtil; @@ -125,8 +126,10 @@ public final class RegistrationLockDialog { dialog.dismiss(); RegistrationLockReminders.scheduleReminder(context, true); - Log.i(TAG, "Pin V1 successfully remembered, scheduling a migration to V2"); - ApplicationDependencies.getJobManager().add(new RegistrationPinV2MigrationJob()); + if (FeatureFlags.kbs()) { + Log.i(TAG, "Pin V1 successfully remembered, scheduling a migration to V2"); + ApplicationDependencies.getJobManager().add(new RegistrationPinV2MigrationJob()); + } } }); } @@ -198,25 +201,32 @@ public final class RegistrationLockDialog { @Override protected Boolean doInBackground(Void... voids) { try { - Log.i(TAG, "Setting pin on KBS"); + if (FeatureFlags.kbs()) { + Log.i(TAG, "Setting pin on KBS"); - KbsValues kbsValues = SignalStore.kbsValues(); - MasterKey masterKey = kbsValues.getOrCreateMasterKey(); - KeyBackupService keyBackupService = ApplicationDependencies.getKeyBackupService(); - KeyBackupService.PinChangeSession pinChangeSession = keyBackupService.newPinChangeSession(); - HashedPin hashedPin = PinHashing.hashPin(pinValue, pinChangeSession); - RegistrationLockData kbsData = pinChangeSession.setPin(hashedPin, masterKey); - RegistrationLockData restoredData = keyBackupService.newRestoreSession(kbsData.getTokenResponse()) - .restorePin(hashedPin); + KbsValues kbsValues = SignalStore.kbsValues(); + MasterKey masterKey = kbsValues.getOrCreateMasterKey(); + KeyBackupService keyBackupService = ApplicationDependencies.getKeyBackupService(); + KeyBackupService.PinChangeSession pinChangeSession = keyBackupService.newPinChangeSession(); + HashedPin hashedPin = PinHashing.hashPin(pinValue, pinChangeSession); + RegistrationLockData kbsData = pinChangeSession.setPin(hashedPin, masterKey); + RegistrationLockData restoredData = keyBackupService.newRestoreSession(kbsData.getTokenResponse()) + .restorePin(hashedPin); - if (!restoredData.getMasterKey().equals(masterKey)) { - throw new AssertionError("Failed to set the pin correctly"); + if (!restoredData.getMasterKey().equals(masterKey)) { + throw new AssertionError("Failed to set the pin correctly"); + } else { + Log.i(TAG, "Set and retrieved pin on KBS successfully"); + } + + kbsValues.setRegistrationLockMasterKey(restoredData, PinHashing.localPinHash(pinValue)); + TextSecurePreferences.clearOldRegistrationLockPin(context); } else { - Log.i(TAG, "Set and retrieved pin on KBS successfully"); + Log.i(TAG, "Setting V1 pin"); + SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager(); + accountManager.setPin(pinValue); + TextSecurePreferences.setDeprecatedRegistrationLockPin(context, pinValue); } - - kbsValues.setRegistrationLockMasterKey(restoredData, PinHashing.localPinHash(pinValue)); - TextSecurePreferences.clearOldRegistrationLockPin(context); TextSecurePreferences.setRegistrationLockLastReminderTime(context, System.currentTimeMillis()); TextSecurePreferences.setRegistrationLockNextReminderInterval(context, RegistrationLockReminders.INITIAL_INTERVAL); return true; @@ -272,13 +282,16 @@ public final class RegistrationLockDialog { @Override protected Boolean doInBackground(Void... voids) { try { - Log.i(TAG, "Removing v2 registration lock pin from server"); - KbsValues kbsValues = SignalStore.kbsValues(); - TokenResponse currentToken = kbsValues.getRegistrationLockTokenResponse(); + KbsValues kbsValues = SignalStore.kbsValues(); - KeyBackupService keyBackupService = ApplicationDependencies.getKeyBackupService(); - keyBackupService.newPinChangeSession(currentToken).removePin(); - kbsValues.clearRegistrationLock(); + if (kbsValues.isV2RegistrationLockEnabled()) { + Log.i(TAG, "Removing v2 registration lock pin from server"); + TokenResponse currentToken = kbsValues.getRegistrationLockTokenResponse(); + + KeyBackupService keyBackupService = ApplicationDependencies.getKeyBackupService(); + keyBackupService.newPinChangeSession(currentToken).removePin(); + kbsValues.clearRegistrationLock(); + } // It is possible a migration has not occurred, in this case, we need to remove the old V1 Pin if (TextSecurePreferences.isV1RegistrationLockEnabled(context)) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/RegistrationPinV2MigrationJob.java b/app/src/main/java/org/thoughtcrime/securesms/migrations/RegistrationPinV2MigrationJob.java index 1003e81b5..b8ea358a5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/migrations/RegistrationPinV2MigrationJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/RegistrationPinV2MigrationJob.java @@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.keyvalue.KbsValues; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.lock.PinHashing; import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.whispersystems.signalservice.api.KeyBackupService; import org.whispersystems.signalservice.api.KeyBackupServicePinException; @@ -56,6 +57,11 @@ public final class RegistrationPinV2MigrationJob extends BaseJob { @Override protected void onRun() throws IOException, UnauthenticatedResponseException, KeyBackupServicePinException { + if (!FeatureFlags.kbs()) { + Log.i(TAG, "Not migrating pin to KBS"); + return; + } + if (!TextSecurePreferences.isV1RegistrationLockEnabled(context)) { Log.i(TAG, "Registration lock disabled"); return; diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/service/CodeVerificationRequest.java b/app/src/main/java/org/thoughtcrime/securesms/registration/service/CodeVerificationRequest.java index 3e7634f04..2dbdc7217 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/service/CodeVerificationRequest.java +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/service/CodeVerificationRequest.java @@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.push.AccountManagerFactory; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.service.DirectoryRefreshListener; import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener; +import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.whispersystems.libsignal.IdentityKeyPair; import org.whispersystems.libsignal.state.PreKeyRecord; @@ -212,8 +213,10 @@ public final class CodeVerificationRequest { //noinspection deprecation Only acceptable place to write the old pin enabled state. TextSecurePreferences.setV1RegistrationLockEnabled(context, pin != null); if (pin != null) { - Log.i(TAG, "Pin V1 successfully entered during registration, scheduling a migration to Pin V2"); - ApplicationDependencies.getJobManager().add(new RegistrationPinV2MigrationJob()); + if (FeatureFlags.kbs()) { + Log.i(TAG, "Pin V1 successfully entered during registration, scheduling a migration to Pin V2"); + ApplicationDependencies.getJobManager().add(new RegistrationPinV2MigrationJob()); + } } } else { SignalStore.kbsValues().setRegistrationLockMasterKey(kbsData, PinHashing.localPinHash(pin)); diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java index 3e1f53b1b..6ea374cac 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java @@ -6,6 +6,7 @@ import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.annimon.stream.Stream; +import com.google.android.collect.Sets; import org.json.JSONException; import org.json.JSONObject; @@ -20,7 +21,6 @@ import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.TreeMap; -import java.util.TreeSet; import java.util.concurrent.TimeUnit; /** @@ -50,6 +50,7 @@ public final class FeatureFlags { private static final String PROFILE_DISPLAY = generateKey("profileDisplay"); private static final String MESSAGE_REQUESTS = generateKey("messageRequests"); private static final String USERNAMES = generateKey("usernames"); + private static final String KBS = generateKey("kbs"); private static final String STORAGE_SERVICE = generateKey("storageService"); private static final String REACTION_SENDING = generateKey("reactionSending"); @@ -73,14 +74,15 @@ public final class FeatureFlags { * will be updated arbitrarily at runtime. This will make values more responsive, but also places * more burden on the reader to ensure that the app experience remains consistent. */ - private static final Set HOT_SWAPPABLE = new TreeSet() {{ - }}; + private static final Set HOT_SWAPPABLE = Sets.newHashSet( + KBS + ); /** * Flags in this set will stay true forever once they receive a true value from a remote config. */ - private static final Set STICKY = new HashSet() {{ - }}; + private static final Set STICKY = Sets.newHashSet( + ); private static final Map REMOTE_VALUES = new TreeMap<>(); @@ -139,9 +141,16 @@ public final class FeatureFlags { return value; } - /** Storage service. */ - public static synchronized boolean storageService() { - return getValue(STORAGE_SERVICE, false); + /** Set or migrate PIN to KBS */ + public static boolean kbs() { + return getValue(KBS, false); + } + + /** Storage service. Requires {@link #kbs()}. */ + public static boolean storageService() { + boolean value = getValue(STORAGE_SERVICE, false); + if (value && !kbs()) throw new MissingFlagRequirementError(); + return value; } /** Send support for reactions. */ diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java index 1ec6e6509..d9be8a536 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java @@ -16,15 +16,12 @@ import androidx.core.app.NotificationCompat; import org.greenrobot.eventbus.EventBus; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver; -import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.lock.RegistrationLockReminders; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.preferences.widgets.NotificationPrivacyPreference; import org.thoughtcrime.securesms.profiles.ProfileName; import org.whispersystems.libsignal.util.Medium; -import org.whispersystems.signalservice.api.RegistrationLockData; import org.whispersystems.signalservice.api.util.UuidUtil; -import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse; import java.io.IOException; import java.security.SecureRandom; @@ -162,8 +159,9 @@ public class TextSecurePreferences { @Deprecated private static final String REGISTRATION_LOCK_PIN_PREF_V1 = "pref_registration_lock_pin"; - private static final String REGISTRATION_LOCK_LAST_REMINDER_TIME = "pref_registration_lock_last_reminder_time_2"; - private static final String REGISTRATION_LOCK_NEXT_REMINDER_INTERVAL = "pref_registration_lock_next_reminder_interval"; + private static final String REGISTRATION_LOCK_LAST_REMINDER_TIME = "pref_registration_lock_last_reminder_time"; + private static final String REGISTRATION_LOCK_LAST_REMINDER_TIME_POST_KBS = "pref_registration_lock_last_reminder_time_post_kbs"; + private static final String REGISTRATION_LOCK_NEXT_REMINDER_INTERVAL = "pref_registration_lock_next_reminder_interval"; private static final String SERVICE_OUTAGE = "pref_service_outage"; private static final String LAST_OUTAGE_CHECK_TIME = "pref_last_outage_check_time"; @@ -271,11 +269,16 @@ public class TextSecurePreferences { } public static long getRegistrationLockLastReminderTime(@NonNull Context context) { - return getLongPreference(context, REGISTRATION_LOCK_LAST_REMINDER_TIME, 0); + return getLongPreference(context, getAppropriateReminderKey(), 0); } public static void setRegistrationLockLastReminderTime(@NonNull Context context, long time) { - setLongPreference(context, REGISTRATION_LOCK_LAST_REMINDER_TIME, time); + setLongPreference(context, getAppropriateReminderKey(), time); + } + + private static String getAppropriateReminderKey() { + return FeatureFlags.kbs() ? REGISTRATION_LOCK_LAST_REMINDER_TIME_POST_KBS + : REGISTRATION_LOCK_LAST_REMINDER_TIME; } public static long getRegistrationLockNextReminderInterval(@NonNull Context context) {