From f2b9bf0b8c6f928679e4b6c73ee8e568344fbb06 Mon Sep 17 00:00:00 2001 From: Alan Evans Date: Fri, 17 Jan 2020 11:14:54 -0500 Subject: [PATCH] Use SignalStore for KBS Values. --- .../jobs/MultiDeviceKeysUpdateJob.java | 5 +- .../securesms/jobs/RefreshAttributesJob.java | 27 ++-- .../securesms/jobs/StorageForcePushJob.java | 7 +- .../securesms/jobs/StorageSyncJob.java | 3 +- .../securesms/keyvalue/KbsValues.java | 82 +++++++++++ .../securesms/keyvalue/SignalStore.java | 4 + .../lock/RegistrationLockDialog.java | 34 +++-- .../lock/RegistrationLockReminders.java | 4 +- .../RegistrationPinV2MigrationJob.java | 13 +- .../AppProtectionPreferenceFragment.java | 14 +- .../service/CodeVerificationRequest.java | 10 +- .../securesms/util/TextSecurePreferences.java | 134 +++--------------- 12 files changed, 169 insertions(+), 168 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/keyvalue/KbsValues.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceKeysUpdateJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceKeysUpdateJob.java index 128d0fd391..9fff09d11f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceKeysUpdateJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceKeysUpdateJob.java @@ -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; diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java index 590ee54696..37afc24362 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java @@ -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; @@ -43,20 +45,19 @@ public class RefreshAttributesJob extends BaseJob { @Override public void onRun() throws IOException { - int registrationId = TextSecurePreferences.getLocalRegistrationId(context); - boolean fetchesMessages = TextSecurePreferences.isFcmDisabled(context); - byte[] unidentifiedAccessKey = UnidentifiedAccessUtil.getSelfUnidentifiedAccessKey(context); - boolean universalUnidentifiedAccess = TextSecurePreferences.isUniversalUnidentifiedAccess(context); - String pin = null; - String registrationLockToken = null; + int registrationId = TextSecurePreferences.getLocalRegistrationId(context); + boolean fetchesMessages = TextSecurePreferences.isFcmDisabled(context); + byte[] unidentifiedAccessKey = UnidentifiedAccessUtil.getSelfUnidentifiedAccessKey(context); + boolean universalUnidentifiedAccess = TextSecurePreferences.isUniversalUnidentifiedAccess(context); + String pin = null; + String registrationLockToken = null; + KbsValues kbsValues = SignalStore.kbsValues(); - if (TextSecurePreferences.isRegistrationLockEnabled(context)) { - if (TextSecurePreferences.hasOldRegistrationLockPin(context)) { - //noinspection deprecation Ok to read here as they have not migrated - pin = TextSecurePreferences.getDeprecatedRegistrationLockPin(context); - } else { - registrationLockToken = TextSecurePreferences.getRegistrationLockToken(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.getDeprecatedV1RegistrationLockPin(context); } SignalServiceAccountManager signalAccountManager = ApplicationDependencies.getSignalServiceAccountManager(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageForcePushJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageForcePushJob.java index 1ed52050a5..fb7686b73c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageForcePushJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageForcePushJob.java @@ -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."); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.java index 857cf00417..141e6de6c6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.java @@ -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."); diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KbsValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KbsValues.java new file mode 100644 index 0000000000..45e3727f9f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KbsValues.java @@ -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); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStore.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStore.java index 7897e91888..9dd55ea5b4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStore.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStore.java @@ -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}. 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 b6001390ce..7be27d9263 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockDialog.java +++ b/app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockDialog.java @@ -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); } } + TextSecurePreferences.clearOldRegistrationLockPin(context); return true; } catch (IOException | UnauthenticatedResponseException e) { Log.w(TAG, e); diff --git a/app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockReminders.java b/app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockReminders.java index 95af0316c3..9a2d1637d4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockReminders.java +++ b/app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockReminders.java @@ -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); 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 c0c5b11bd7..a13263ffa5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/migrations/RegistrationPinV2MigrationJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/RegistrationPinV2MigrationJob.java @@ -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; diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java index 4f062f15ad..eaf0c89b6d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java @@ -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()); @@ -211,18 +212,19 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment } public static CharSequence getSummary(Context context) { - 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); + 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); 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 0716c9df97..427489054a 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 @@ -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) { 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 8bfe6ab056..36f9507432 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java @@ -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) {