KBS remote feature flag.

This commit is contained in:
Alan Evans 2020-01-24 18:38:48 -05:00 committed by Greyson Parrelli
parent ba6e1ab15a
commit fea2b6253f
5 changed files with 74 additions and 40 deletions

View File

@ -35,6 +35,7 @@ import org.thoughtcrime.securesms.keyvalue.KbsValues;
import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.migrations.RegistrationPinV2MigrationJob; import org.thoughtcrime.securesms.migrations.RegistrationPinV2MigrationJob;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ThemeUtil; import org.thoughtcrime.securesms.util.ThemeUtil;
@ -125,9 +126,11 @@ public final class RegistrationLockDialog {
dialog.dismiss(); dialog.dismiss();
RegistrationLockReminders.scheduleReminder(context, true); RegistrationLockReminders.scheduleReminder(context, true);
if (FeatureFlags.kbs()) {
Log.i(TAG, "Pin V1 successfully remembered, scheduling a migration to V2"); Log.i(TAG, "Pin V1 successfully remembered, scheduling a migration to V2");
ApplicationDependencies.getJobManager().add(new RegistrationPinV2MigrationJob()); ApplicationDependencies.getJobManager().add(new RegistrationPinV2MigrationJob());
} }
}
}); });
} }
@ -198,6 +201,7 @@ public final class RegistrationLockDialog {
@Override @Override
protected Boolean doInBackground(Void... voids) { protected Boolean doInBackground(Void... voids) {
try { try {
if (FeatureFlags.kbs()) {
Log.i(TAG, "Setting pin on KBS"); Log.i(TAG, "Setting pin on KBS");
KbsValues kbsValues = SignalStore.kbsValues(); KbsValues kbsValues = SignalStore.kbsValues();
@ -217,6 +221,12 @@ public final class RegistrationLockDialog {
kbsValues.setRegistrationLockMasterKey(restoredData, PinHashing.localPinHash(pinValue)); kbsValues.setRegistrationLockMasterKey(restoredData, PinHashing.localPinHash(pinValue));
TextSecurePreferences.clearOldRegistrationLockPin(context); TextSecurePreferences.clearOldRegistrationLockPin(context);
} else {
Log.i(TAG, "Setting V1 pin");
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
accountManager.setPin(pinValue);
TextSecurePreferences.setDeprecatedRegistrationLockPin(context, pinValue);
}
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;
@ -272,13 +282,16 @@ public final class RegistrationLockDialog {
@Override @Override
protected Boolean doInBackground(Void... voids) { protected Boolean doInBackground(Void... voids) {
try { try {
Log.i(TAG, "Removing v2 registration lock pin from server");
KbsValues kbsValues = SignalStore.kbsValues(); KbsValues kbsValues = SignalStore.kbsValues();
if (kbsValues.isV2RegistrationLockEnabled()) {
Log.i(TAG, "Removing v2 registration lock pin from server");
TokenResponse currentToken = kbsValues.getRegistrationLockTokenResponse(); TokenResponse currentToken = kbsValues.getRegistrationLockTokenResponse();
KeyBackupService keyBackupService = ApplicationDependencies.getKeyBackupService(); KeyBackupService keyBackupService = ApplicationDependencies.getKeyBackupService();
keyBackupService.newPinChangeSession(currentToken).removePin(); keyBackupService.newPinChangeSession(currentToken).removePin();
kbsValues.clearRegistrationLock(); kbsValues.clearRegistrationLock();
}
// It is possible a migration has not occurred, in this case, we need to remove the old V1 Pin // It is possible a migration has not occurred, in this case, we need to remove the old V1 Pin
if (TextSecurePreferences.isV1RegistrationLockEnabled(context)) { if (TextSecurePreferences.isV1RegistrationLockEnabled(context)) {

View File

@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.keyvalue.KbsValues;
import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.lock.PinHashing; import org.thoughtcrime.securesms.lock.PinHashing;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.FeatureFlags;
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;
@ -56,6 +57,11 @@ public final class RegistrationPinV2MigrationJob extends BaseJob {
@Override @Override
protected void onRun() throws IOException, UnauthenticatedResponseException, KeyBackupServicePinException { protected void onRun() throws IOException, UnauthenticatedResponseException, KeyBackupServicePinException {
if (!FeatureFlags.kbs()) {
Log.i(TAG, "Not migrating pin to KBS");
return;
}
if (!TextSecurePreferences.isV1RegistrationLockEnabled(context)) { if (!TextSecurePreferences.isV1RegistrationLockEnabled(context)) {
Log.i(TAG, "Registration lock disabled"); Log.i(TAG, "Registration lock disabled");
return; return;

View File

@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.push.AccountManagerFactory;
import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.service.DirectoryRefreshListener; import org.thoughtcrime.securesms.service.DirectoryRefreshListener;
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener; import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.IdentityKeyPair; import org.whispersystems.libsignal.IdentityKeyPair;
import org.whispersystems.libsignal.state.PreKeyRecord; import org.whispersystems.libsignal.state.PreKeyRecord;
@ -212,9 +213,11 @@ public final class CodeVerificationRequest {
//noinspection deprecation Only acceptable place to write the old pin enabled state. //noinspection deprecation Only acceptable place to write the old pin enabled state.
TextSecurePreferences.setV1RegistrationLockEnabled(context, pin != null); TextSecurePreferences.setV1RegistrationLockEnabled(context, pin != null);
if (pin != null) { if (pin != null) {
if (FeatureFlags.kbs()) {
Log.i(TAG, "Pin V1 successfully entered during registration, scheduling a migration to Pin V2"); Log.i(TAG, "Pin V1 successfully entered during registration, scheduling a migration to Pin V2");
ApplicationDependencies.getJobManager().add(new RegistrationPinV2MigrationJob()); ApplicationDependencies.getJobManager().add(new RegistrationPinV2MigrationJob());
} }
}
} else { } else {
SignalStore.kbsValues().setRegistrationLockMasterKey(kbsData, PinHashing.localPinHash(pin)); SignalStore.kbsValues().setRegistrationLockMasterKey(kbsData, PinHashing.localPinHash(pin));
repostPinToResetTries(context, pin, kbsData); repostPinToResetTries(context, pin, kbsData);

View File

@ -6,6 +6,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
import com.google.android.collect.Sets;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
@ -20,7 +21,6 @@ import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit; 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 PROFILE_DISPLAY = generateKey("profileDisplay");
private static final String MESSAGE_REQUESTS = generateKey("messageRequests"); private static final String MESSAGE_REQUESTS = generateKey("messageRequests");
private static final String USERNAMES = generateKey("usernames"); 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 STORAGE_SERVICE = generateKey("storageService");
private static final String REACTION_SENDING = generateKey("reactionSending"); 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 * 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. * more burden on the reader to ensure that the app experience remains consistent.
*/ */
private static final Set<String> HOT_SWAPPABLE = new TreeSet<String>() {{ private static final Set<String> HOT_SWAPPABLE = Sets.newHashSet(
}}; KBS
);
/** /**
* Flags in this set will stay true forever once they receive a true value from a remote config. * Flags in this set will stay true forever once they receive a true value from a remote config.
*/ */
private static final Set<String> STICKY = new HashSet<String>() {{ private static final Set<String> STICKY = Sets.newHashSet(
}}; );
private static final Map<String, Boolean> REMOTE_VALUES = new TreeMap<>(); private static final Map<String, Boolean> REMOTE_VALUES = new TreeMap<>();
@ -139,9 +141,16 @@ public final class FeatureFlags {
return value; return value;
} }
/** Storage service. */ /** Set or migrate PIN to KBS */
public static synchronized boolean storageService() { public static boolean kbs() {
return getValue(STORAGE_SERVICE, false); 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. */ /** Send support for reactions. */

View File

@ -16,15 +16,12 @@ import androidx.core.app.NotificationCompat;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver; import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.lock.RegistrationLockReminders; import org.thoughtcrime.securesms.lock.RegistrationLockReminders;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.preferences.widgets.NotificationPrivacyPreference; import org.thoughtcrime.securesms.preferences.widgets.NotificationPrivacyPreference;
import org.thoughtcrime.securesms.profiles.ProfileName; import org.thoughtcrime.securesms.profiles.ProfileName;
import org.whispersystems.libsignal.util.Medium; import org.whispersystems.libsignal.util.Medium;
import org.whispersystems.signalservice.api.RegistrationLockData;
import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
import java.io.IOException; import java.io.IOException;
import java.security.SecureRandom; import java.security.SecureRandom;
@ -162,7 +159,8 @@ public class TextSecurePreferences {
@Deprecated @Deprecated
private static final String REGISTRATION_LOCK_PIN_PREF_V1 = "pref_registration_lock_pin"; 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_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 REGISTRATION_LOCK_NEXT_REMINDER_INTERVAL = "pref_registration_lock_next_reminder_interval";
private static final String SERVICE_OUTAGE = "pref_service_outage"; private static final String SERVICE_OUTAGE = "pref_service_outage";
@ -271,11 +269,16 @@ public class TextSecurePreferences {
} }
public static long getRegistrationLockLastReminderTime(@NonNull Context context) { 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) { 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) { public static long getRegistrationLockNextReminderInterval(@NonNull Context context) {