Use SignalStore for KBS Values.

This commit is contained in:
Alan Evans 2020-01-17 11:14:54 -05:00 committed by Greyson Parrelli
parent fadcc606f8
commit f2b9bf0b8c
12 changed files with 169 additions and 168 deletions

View File

@ -8,17 +8,16 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.multidevice.ConfigurationMessage;
import org.whispersystems.signalservice.api.messages.multidevice.KeysMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import org.whispersystems.signalservice.api.storage.SignalStorageUtil;
import org.whispersystems.signalservice.internal.configuration.SignalStorageUrl;
import java.io.IOException;
@ -61,7 +60,7 @@ public class MultiDeviceKeysUpdateJob extends BaseJob {
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
byte[] masterKey = TextSecurePreferences.getMasterKey(context);
byte[] masterKey = SignalStore.kbsValues().getMasterKey();
byte[] storageServiceKey = masterKey != null ? SignalStorageUtil.computeStorageServiceKey(masterKey)
: null;

View File

@ -7,6 +7,8 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.keyvalue.KbsValues;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
@ -49,14 +51,13 @@ public class RefreshAttributesJob extends BaseJob {
boolean universalUnidentifiedAccess = TextSecurePreferences.isUniversalUnidentifiedAccess(context);
String pin = null;
String registrationLockToken = null;
KbsValues kbsValues = SignalStore.kbsValues();
if (TextSecurePreferences.isRegistrationLockEnabled(context)) {
if (TextSecurePreferences.hasOldRegistrationLockPin(context)) {
if (kbsValues.isV2RegistrationLockEnabled()) {
registrationLockToken = kbsValues.getRegistrationLockToken();
} else if (TextSecurePreferences.isV1RegistrationLockEnabled(context)) {
//noinspection deprecation Ok to read here as they have not migrated
pin = TextSecurePreferences.getDeprecatedRegistrationLockPin(context);
} else {
registrationLockToken = TextSecurePreferences.getRegistrationLockToken(context);
}
pin = TextSecurePreferences.getDeprecatedV1RegistrationLockPin(context);
}
SignalServiceAccountManager signalAccountManager = ApplicationDependencies.getSignalServiceAccountManager();

View File

@ -12,22 +12,19 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import org.whispersystems.signalservice.api.storage.SignalContactRecord;
import org.whispersystems.signalservice.api.storage.SignalStorageManifest;
import org.whispersystems.signalservice.api.storage.SignalStorageRecord;
import org.whispersystems.signalservice.api.storage.SignalStorageUtil;
import org.whispersystems.signalservice.internal.storage.protos.StorageRecord;
import java.io.IOException;
import java.util.ArrayList;
@ -72,7 +69,7 @@ public class StorageForcePushJob extends BaseJob {
protected void onRun() throws IOException, RetryLaterException {
if (!FeatureFlags.STORAGE_SERVICE) throw new AssertionError();
byte[] kbsMasterKey = TextSecurePreferences.getMasterKey(context);
byte[] kbsMasterKey = SignalStore.kbsValues().getMasterKey();
if (kbsMasterKey == null) {
Log.w(TAG, "No KBS master key is set! Must abort.");

View File

@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.transport.RetryLaterException;
@ -109,7 +110,7 @@ public class StorageSyncJob extends BaseJob {
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
StorageKeyDatabase storageKeyDatabase = DatabaseFactory.getStorageKeyDatabase(context);
byte[] kbsMasterKey = TextSecurePreferences.getMasterKey(context);
byte[] kbsMasterKey = SignalStore.kbsValues().getMasterKey();
if (kbsMasterKey == null) {
Log.w(TAG, "No KBS master key is set! Must abort.");

View File

@ -0,0 +1,82 @@
package org.thoughtcrime.securesms.keyvalue;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.whispersystems.signalservice.api.RegistrationLockData;
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
import org.whispersystems.signalservice.internal.registrationpin.PinStretcher;
import java.io.IOException;
public final class KbsValues {
private static final String REGISTRATION_LOCK_PREF_V2 = "kbs.registration_lock_v2";
private static final String REGISTRATION_LOCK_TOKEN_PREF = "kbs.registration_lock_token";
private static final String REGISTRATION_LOCK_PIN_KEY_2_PREF = "kbs.registration_lock_pin_key_2";
private static final String REGISTRATION_LOCK_MASTER_KEY = "kbs.registration_lock_master_key";
private static final String REGISTRATION_LOCK_TOKEN_RESPONSE = "kbs.registration_lock_token_response";
private final KeyValueStore store;
KbsValues(KeyValueStore store) {
this.store = store;
}
public void setRegistrationLockMasterKey(@Nullable RegistrationLockData registrationLockData) {
KeyValueStore.Writer editor = store.beginWrite();
if (registrationLockData == null) {
editor.remove(REGISTRATION_LOCK_PREF_V2)
.remove(REGISTRATION_LOCK_TOKEN_RESPONSE)
.remove(REGISTRATION_LOCK_MASTER_KEY)
.remove(REGISTRATION_LOCK_TOKEN_PREF)
.remove(REGISTRATION_LOCK_PIN_KEY_2_PREF);
} else {
PinStretcher.MasterKey masterKey = registrationLockData.getMasterKey();
String tokenResponse;
try {
tokenResponse = JsonUtils.toJson(registrationLockData.getTokenResponse());
} catch (IOException e) {
throw new AssertionError(e);
}
editor.putBoolean(REGISTRATION_LOCK_PREF_V2, true)
.putString(REGISTRATION_LOCK_TOKEN_RESPONSE, tokenResponse)
.putBlob(REGISTRATION_LOCK_MASTER_KEY, masterKey.getMasterKey())
.putString(REGISTRATION_LOCK_TOKEN_PREF, masterKey.getRegistrationLock())
.putBlob(REGISTRATION_LOCK_PIN_KEY_2_PREF, masterKey.getPinKey2());
}
editor.commit();
}
public byte[] getMasterKey() {
return store.getBlob(REGISTRATION_LOCK_MASTER_KEY, null);
}
public @Nullable String getRegistrationLockToken() {
return store.getString(REGISTRATION_LOCK_TOKEN_PREF, null);
}
public @Nullable byte[] getRegistrationLockPinKey2() {
return store.getBlob(REGISTRATION_LOCK_PIN_KEY_2_PREF, null);
}
public boolean isV2RegistrationLockEnabled() {
return store.getBoolean(REGISTRATION_LOCK_PREF_V2, false);
}
public @Nullable
TokenResponse getRegistrationLockTokenResponse() {
String token = store.getString(REGISTRATION_LOCK_TOKEN_RESPONSE, null);
if (token == null) return null;
try {
return JsonUtils.fromJson(token, TokenResponse.class);
} catch (IOException e) {
throw new AssertionError(e);
}
}
}

View File

@ -12,6 +12,10 @@ public final class SignalStore {
private SignalStore() {}
public static KbsValues kbsValues() {
return new KbsValues(getStore());
}
/**
* Ensures any pending writes are finished. Only intended to be called by
* {@link SignalUncaughtExceptionHandler}.

View File

@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.lock;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Typeface;
@ -32,6 +31,8 @@ import androidx.appcompat.app.AlertDialog;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.SwitchPreferenceCompat;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
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;
@ -57,8 +58,8 @@ public final class RegistrationLockDialog {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
if (!RegistrationLockReminders.needsReminder(context)) return;
if (!TextSecurePreferences.hasOldRegistrationLockPin(context) &&
TextUtils.isEmpty(TextSecurePreferences.getRegistrationLockToken(context))) {
if (!TextSecurePreferences.isV1RegistrationLockEnabled(context) &&
TextUtils.isEmpty(SignalStore.kbsValues().getRegistrationLockToken())) {
// Neither v1 or v2 to check against
Log.w(TAG, "Reg lock enabled, but no pin stored to verify against");
return;
@ -107,14 +108,14 @@ public final class RegistrationLockDialog {
reminder.setText(new SpannableStringBuilder(reminderIntro).append(" ").append(reminderText).append(" ").append(forgotText));
reminder.setMovementMethod(LinkMovementMethod.getInstance());
pinEditText.addTextChangedListener(TextSecurePreferences.hasOldRegistrationLockPin(context)
? getV1PinWatcher(context, dialog)
: getV2PinWatcher(context, dialog));
pinEditText.addTextChangedListener(SignalStore.kbsValues().isV2RegistrationLockEnabled()
? getV2PinWatcher(context, dialog)
: getV1PinWatcher(context, dialog));
}
private static TextWatcher getV1PinWatcher(@NonNull Context context, AlertDialog dialog) {
//noinspection deprecation Acceptable to check the old pin in a reminder on a non-migrated system.
String pin = TextSecurePreferences.getDeprecatedRegistrationLockPin(context);
String pin = TextSecurePreferences.getDeprecatedV1RegistrationLockPin(context);
return new TextWatcher() {
@ -139,9 +140,10 @@ public final class RegistrationLockDialog {
}
private static TextWatcher getV2PinWatcher(@NonNull Context context, AlertDialog dialog) {
String registrationLockToken = TextSecurePreferences.getRegistrationLockToken(context);
byte[] pinKey2 = TextSecurePreferences.getRegistrationLockPinKey2(context);
TokenResponse registrationLockTokenResponse = TextSecurePreferences.getRegistrationLockTokenResponse(context);
KbsValues kbsValues = SignalStore.kbsValues();
String registrationLockToken = kbsValues.getRegistrationLockToken();
byte[] pinKey2 = kbsValues.getRegistrationLockPinKey2();
TokenResponse registrationLockTokenResponse = kbsValues.getRegistrationLockTokenResponse();
if (registrationLockToken == null) throw new AssertionError("No V2 reg lock token set at time of reminder");
if (pinKey2 == null) throw new AssertionError("No pin key2 set at time of reminder");
@ -238,7 +240,8 @@ public final class RegistrationLockDialog {
Log.i(TAG, "Set and retrieved pin on KBS successfully");
}
TextSecurePreferences.setRegistrationLockMasterKey(context, restoredData, System.currentTimeMillis());
SignalStore.kbsValues().setRegistrationLockMasterKey(restoredData);
TextSecurePreferences.clearOldRegistrationLockPin(context);
TextSecurePreferences.setRegistrationLockLastReminderTime(context, System.currentTimeMillis());
TextSecurePreferences.setRegistrationLockNextReminderInterval(context, RegistrationLockReminders.INITIAL_INTERVAL);
}
@ -300,19 +303,20 @@ public final class RegistrationLockDialog {
ApplicationDependencies.getSignalServiceAccountManager().removeV1Pin();
} else {
Log.i(TAG, "Removing v2 registration lock pin from server");
TokenResponse currentToken = TextSecurePreferences.getRegistrationLockTokenResponse(context);
KbsValues kbsValues = SignalStore.kbsValues();
TokenResponse currentToken = kbsValues.getRegistrationLockTokenResponse();
KeyBackupService keyBackupService = ApplicationDependencies.getKeyBackupService();
keyBackupService.newPinChangeSession(currentToken).removePin();
TextSecurePreferences.setRegistrationLockMasterKey(context, null, System.currentTimeMillis());
kbsValues.setRegistrationLockMasterKey(null);
// It is possible a migration has not occurred, in this case, we need to remove the old V1 Pin
if (TextSecurePreferences.hasOldRegistrationLockPin(context)) {
if (TextSecurePreferences.isV1RegistrationLockEnabled(context)) {
Log.i(TAG, "Removing v1 registration lock pin from server");
ApplicationDependencies.getSignalServiceAccountManager().removeV1Pin();
}
}
TextSecurePreferences.clearOldRegistrationLockPin(context);
}
}
return true;
} catch (IOException | UnauthenticatedResponseException e) {
Log.w(TAG, e);

View File

@ -5,6 +5,7 @@ import android.content.Context;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.util.NavigableSet;
@ -24,7 +25,8 @@ public class RegistrationLockReminders {
public static final long INITIAL_INTERVAL = INTERVALS.first();
public static boolean needsReminder(@NonNull Context context) {
if (!TextSecurePreferences.isRegistrationLockEnabled(context)) return false;
if (!TextSecurePreferences.isV1RegistrationLockEnabled(context) &&
!SignalStore.kbsValues().isV2RegistrationLockEnabled()) return false;
long lastReminderTime = TextSecurePreferences.getRegistrationLockLastReminderTime(context);
long nextIntervalTime = TextSecurePreferences.getRegistrationLockNextReminderInterval(context);

View File

@ -9,6 +9,7 @@ import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.jobs.BaseJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -54,18 +55,13 @@ public final class RegistrationPinV2MigrationJob extends BaseJob {
return;
}
if (!TextSecurePreferences.isRegistrationLockEnabled(context)) {
if (!TextSecurePreferences.isV1RegistrationLockEnabled(context)) {
Log.i(TAG, "Registration lock disabled");
return;
}
if (!TextSecurePreferences.hasOldRegistrationLockPin(context)) {
Log.i(TAG, "No old pin to migrate");
return;
}
//noinspection deprecation Only acceptable place to read the old pin.
String registrationLockPin = TextSecurePreferences.getDeprecatedRegistrationLockPin(context);
String registrationLockPin = TextSecurePreferences.getDeprecatedV1RegistrationLockPin(context);
if (registrationLockPin == null | TextUtils.isEmpty(registrationLockPin)) {
Log.i(TAG, "No old pin to migrate");
@ -79,7 +75,8 @@ public final class RegistrationPinV2MigrationJob extends BaseJob {
.newPinChangeSession()
.setPin(registrationLockPin);
TextSecurePreferences.setRegistrationLockMasterKey(context, registrationPinV2Key, System.currentTimeMillis());
SignalStore.kbsValues().setRegistrationLockMasterKey(registrationPinV2Key);
TextSecurePreferences.clearOldRegistrationLockPin(context);
} catch (InvalidPinException e) {
Log.w(TAG, "The V1 pin cannot be migrated.", e);
return;

View File

@ -21,6 +21,7 @@ import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob;
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.lock.RegistrationLockDialog;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.CommunicationActions;
@ -44,7 +45,7 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
disablePassphrase = (CheckBoxPreference) this.findPreference("pref_enable_passphrase_temporary");
this.findPreference(TextSecurePreferences.REGISTRATION_LOCK_PREF).setOnPreferenceClickListener(new AccountLockClickListener());
this.findPreference(TextSecurePreferences.REGISTRATION_LOCK_PREF_V1).setOnPreferenceClickListener(new AccountLockClickListener());
this.findPreference(TextSecurePreferences.SCREEN_LOCK).setOnPreferenceChangeListener(new ScreenLockListener());
this.findPreference(TextSecurePreferences.SCREEN_LOCK_TIMEOUT).setOnPreferenceClickListener(new ScreenLockTimeoutListener());
@ -214,15 +215,16 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
final int privacySummaryResId = R.string.ApplicationPreferencesActivity_privacy_summary;
final String onRes = context.getString(R.string.ApplicationPreferencesActivity_on);
final String offRes = context.getString(R.string.ApplicationPreferencesActivity_off);
boolean registrationLockEnabled = TextSecurePreferences.isV1RegistrationLockEnabled(context) || SignalStore.kbsValues().isV2RegistrationLockEnabled();
if (TextSecurePreferences.isPasswordDisabled(context) && !TextSecurePreferences.isScreenLockEnabled(context)) {
if (TextSecurePreferences.isRegistrationLockEnabled(context)) {
if (registrationLockEnabled) {
return context.getString(privacySummaryResId, offRes, onRes);
} else {
return context.getString(privacySummaryResId, offRes, offRes);
}
} else {
if (TextSecurePreferences.isRegistrationLockEnabled(context)) {
if (registrationLockEnabled) {
return context.getString(privacySummaryResId, onRes, onRes);
} else {
return context.getString(privacySummaryResId, onRes, offRes);

View File

@ -17,11 +17,11 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
import org.thoughtcrime.securesms.jobs.RotateCertificateJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.lock.RegistrationLockReminders;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.migrations.RegistrationPinV2MigrationJob;
import org.thoughtcrime.securesms.push.AccountManagerFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.service.DirectoryRefreshListener;
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
@ -203,10 +203,12 @@ public final class CodeVerificationRequest {
TextSecurePreferences.setSignedPreKeyRegistered(context, true);
TextSecurePreferences.setPromptedPushRegistration(context, true);
TextSecurePreferences.setUnauthorizedReceived(context, false);
TextSecurePreferences.setRegistrationLockMasterKey(context, kbsData, System.currentTimeMillis());
SignalStore.kbsValues().setRegistrationLockMasterKey(kbsData);
if (kbsData == null) {
//noinspection deprecation Only acceptable place to write the old pin.
TextSecurePreferences.setDeprecatedRegistrationLockPin(context, pin);
//noinspection deprecation Only acceptable place to write the old pin enabled state.
TextSecurePreferences.setV1RegistrationLockEnabled(context, pin != null);
if (pin != null) {
if (FeatureFlags.KBS) {
Log.i(TAG, "Pin V1 successfully entered during registration, scheduling a migration to Pin V2");
@ -216,7 +218,6 @@ public final class CodeVerificationRequest {
} else {
repostPinToResetTries(context, pin, kbsData);
}
TextSecurePreferences.setRegistrationLockEnabled(context, pin != null);
if (pin != null) {
TextSecurePreferences.setRegistrationLockLastReminderTime(context, System.currentTimeMillis());
TextSecurePreferences.setRegistrationLockNextReminderInterval(context, RegistrationLockReminders.INITIAL_INTERVAL);
@ -231,7 +232,8 @@ public final class CodeVerificationRequest {
try {
RegistrationLockData newData = keyBackupService.newPinChangeSession(kbsData.getTokenResponse())
.setPin(pin);
TextSecurePreferences.setRegistrationLockMasterKey(context, newData, System.currentTimeMillis());
SignalStore.kbsValues().setRegistrationLockMasterKey(newData);
TextSecurePreferences.clearOldRegistrationLockPin(context);
} catch (IOException e) {
Log.w(TAG, "May have failed to reset pin attempts!", e);
} catch (UnauthenticatedResponseException e) {

View File

@ -7,17 +7,16 @@ import android.net.Uri;
import android.os.Build;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.text.TextUtils;
import androidx.annotation.ArrayRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat;
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;
@ -25,7 +24,6 @@ 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 org.whispersystems.signalservice.internal.registrationpin.PinStretcher;
import java.io.IOException;
import java.security.SecureRandom;
@ -158,16 +156,13 @@ public class TextSecurePreferences {
public static final String SCREEN_LOCK = "pref_android_screen_lock";
public static final String SCREEN_LOCK_TIMEOUT = "pref_android_screen_lock_timeout";
public static final String REGISTRATION_LOCK_PREF = "pref_registration_lock";
private static final String REGISTRATION_LOCK_PIN_PREF = "pref_registration_lock_pin";
private static final String REGISTRATION_LOCK_TOKEN_PREF = "pref_registration_lock_token";
private static final String REGISTRATION_LOCK_PIN_KEY_2_PREF = "pref_registration_lock_pin_key_2";
private static final String REGISTRATION_LOCK_MASTER_KEY = "pref_registration_lock_master_key";
private static final String REGISTRATION_LOCK_TOKEN_RESPONSE = "pref_registration_lock_token_response";
@Deprecated
public static final String REGISTRATION_LOCK_PREF_V1 = "pref_registration_lock";
@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";
private static final String REGISTRATION_LOCK_NEXT_REMINDER_INTERVAL = "pref_registration_lock_next_reminder_interval";
private static final String REGISTRATION_LOCK_SERVER_CONSISTENT = "pref_registration_lock_server_consistent";
private static final String REGISTRATION_LOCK_SERVER_CONSISTENT_TIME = "pref_registration_lock_server_consistent_time";
private static final String SERVICE_OUTAGE = "pref_service_outage";
private static final String LAST_OUTAGE_CHECK_TIME = "pref_last_outage_check_time";
@ -234,31 +229,34 @@ public class TextSecurePreferences {
setLongPreference(context, SCREEN_LOCK_TIMEOUT, value);
}
public static boolean isRegistrationLockEnabled(@NonNull Context context) {
return getBooleanPreference(context, REGISTRATION_LOCK_PREF, false);
public static boolean isV1RegistrationLockEnabled(@NonNull Context context) {
//noinspection deprecation
return getBooleanPreference(context, REGISTRATION_LOCK_PREF_V1, false);
}
public static void setRegistrationLockEnabled(@NonNull Context context, boolean value) {
setBooleanPreference(context, REGISTRATION_LOCK_PREF, value);
/**
* @deprecated Use only during re-reg where user had pinV1.
*/
@Deprecated
public static void setV1RegistrationLockEnabled(@NonNull Context context, boolean value) {
//noinspection deprecation
setBooleanPreference(context, REGISTRATION_LOCK_PREF_V1, value);
}
/**
* @deprecated Use only for migrations to the Key Backup Store registration pinV2.
*/
@Deprecated
public static @Nullable String getDeprecatedRegistrationLockPin(@NonNull Context context) {
return getStringPreference(context, REGISTRATION_LOCK_PIN_PREF, null);
}
public static boolean hasOldRegistrationLockPin(@NonNull Context context) {
public static @Nullable String getDeprecatedV1RegistrationLockPin(@NonNull Context context) {
//noinspection deprecation
return !TextUtils.isEmpty(getDeprecatedRegistrationLockPin(context));
return getStringPreference(context, REGISTRATION_LOCK_PIN_PREF_V1, null);
}
public static void clearOldRegistrationLockPin(@NonNull Context context) {
//noinspection deprecation
PreferenceManager.getDefaultSharedPreferences(context)
.edit()
.remove(REGISTRATION_LOCK_PIN_PREF)
.remove(REGISTRATION_LOCK_PIN_PREF_V1)
.apply();
}
@ -267,96 +265,8 @@ public class TextSecurePreferences {
*/
@Deprecated
public static void setDeprecatedRegistrationLockPin(@NonNull Context context, String pin) {
setStringPreference(context, REGISTRATION_LOCK_PIN_PREF, pin);
}
/** Clears old pin preference at same time if non-null */
public static void setRegistrationLockMasterKey(@NonNull Context context, @Nullable RegistrationLockData registrationLockData, long time) {
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context)
.edit()
.putBoolean(REGISTRATION_LOCK_SERVER_CONSISTENT, true)
.putLong(REGISTRATION_LOCK_SERVER_CONSISTENT_TIME, time);
if (registrationLockData == null) {
editor.remove(REGISTRATION_LOCK_TOKEN_RESPONSE)
.remove(REGISTRATION_LOCK_MASTER_KEY)
.remove(REGISTRATION_LOCK_TOKEN_PREF)
.remove(REGISTRATION_LOCK_PIN_KEY_2_PREF);
} else {
PinStretcher.MasterKey masterKey = registrationLockData.getMasterKey();
String tokenResponse;
try {
tokenResponse = JsonUtils.toJson(registrationLockData.getTokenResponse());
} catch (IOException e) {
throw new AssertionError(e);
}
editor.remove(REGISTRATION_LOCK_PIN_PREF) // Removal of V1 pin
.putBoolean(REGISTRATION_LOCK_PREF, true)
.putString(REGISTRATION_LOCK_TOKEN_RESPONSE, tokenResponse)
.putString(REGISTRATION_LOCK_MASTER_KEY, Base64.encodeBytes(masterKey.getMasterKey()))
.putString(REGISTRATION_LOCK_TOKEN_PREF, masterKey.getRegistrationLock())
.putString(REGISTRATION_LOCK_PIN_KEY_2_PREF, Base64.encodeBytes(masterKey.getPinKey2()));
}
editor.apply();
}
public static byte[] getMasterKey(@NonNull Context context) {
String key = getStringPreference(context, REGISTRATION_LOCK_MASTER_KEY, null);
if (key == null) {
return null;
}
try {
return Base64.decode(key);
} catch (IOException e) {
throw new AssertionError(e);
}
}
public static void setRegistrationLockServerConsistent(@NonNull Context context, boolean consistent, long time) {
PreferenceManager.getDefaultSharedPreferences(context)
.edit()
.putBoolean(REGISTRATION_LOCK_SERVER_CONSISTENT, consistent)
.putLong(REGISTRATION_LOCK_SERVER_CONSISTENT_TIME, time)
.apply();
}
public static @Nullable String getRegistrationLockToken(@NonNull Context context) {
return getStringPreference(context, REGISTRATION_LOCK_TOKEN_PREF, null);
}
public static void setRegistrationLockTokenResponse(@NonNull Context context, @NonNull TokenResponse tokenResponse) {
String tokenResponseString;
try {
tokenResponseString = JsonUtils.toJson(tokenResponse);
} catch (IOException e) {
throw new AssertionError(e);
}
setStringPreference(context, REGISTRATION_LOCK_TOKEN_RESPONSE, tokenResponseString);
}
public static @Nullable TokenResponse getRegistrationLockTokenResponse(@NonNull Context context) {
String token = getStringPreference(context, REGISTRATION_LOCK_TOKEN_RESPONSE, null);
if (token == null) return null;
try {
return JsonUtils.fromJson(token, TokenResponse.class);
} catch (IOException e) {
Log.w(TAG, e);
return null;
}
}
public static @Nullable byte[] getRegistrationLockPinKey2(@NonNull Context context){
String pinKey2 = getStringPreference(context, REGISTRATION_LOCK_PIN_KEY_2_PREF, null);
try {
return pinKey2 != null ? Base64.decode(pinKey2) : null;
} catch (IOException e) {
Log.w(TAG, e);
return null;
}
//noinspection deprecation
setStringPreference(context, REGISTRATION_LOCK_PIN_PREF_V1, pin);
}
public static long getRegistrationLockLastReminderTime(@NonNull Context context) {