diff --git a/app/build.gradle b/app/build.gradle index 5aee07c72d..3245de12d6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -122,9 +122,9 @@ android { buildConfigField "String", "CONTENT_PROXY_HOST", "\"contentproxy.signal.org\"" buildConfigField "int", "CONTENT_PROXY_PORT", "443" buildConfigField "String", "SIGNAL_AGENT", "\"OWA\"" - buildConfigField "String", "MRENCLAVE", "\"cd6cfc342937b23b1bdd3bbf9721aa5615ac9ff50a75c5527d441cd3276826c9\"" - buildConfigField "String", "KEY_BACKUP_ENCLAVE_NAME", "\"fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe\"" - buildConfigField "String", "KEY_BACKUP_MRENCLAVE", "\"a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87\"" + buildConfigField "String", "CDS_MRENCLAVE", "\"cd6cfc342937b23b1bdd3bbf9721aa5615ac9ff50a75c5527d441cd3276826c9\"" + buildConfigField "String", "KBS_ENCLAVE_NAME", "\"fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe\"" + buildConfigField "String", "KBS_MRENCLAVE", "\"a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87\"" buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF\"" buildConfigField "String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"\"" buildConfigField "String[]", "LANGUAGES", "new String[]{\"" + autoResConfig().collect { s -> s.replace('-r', '_') }.join('", "') + '"}' @@ -198,8 +198,8 @@ android { buildConfigField "String", "SIGNAL_CDN2_URL", "\"https://cdn2-staging.signal.org\"" buildConfigField "String", "SIGNAL_CONTACT_DISCOVERY_URL", "\"https://api-staging.directory.signal.org\"" buildConfigField "String", "SIGNAL_KEY_BACKUP_URL", "\"https://api-staging.backup.signal.org\"" - buildConfigField "String", "MRENCLAVE", "\"ba4ebb438bc07713819ee6c98d94037747006d7df63fc9e44d2d6f1fec962a79\"" - buildConfigField "String", "KEY_BACKUP_ENCLAVE_NAME", "\"a1e9c1d3f352b5c4f0fc7a421b98119e60e5ff703c28fbea85c66bfa7306deab\"" + buildConfigField "String", "CDS_MRENCLAVE", "\"ba4ebb438bc07713819ee6c98d94037747006d7df63fc9e44d2d6f1fec962a79\"" + buildConfigField "String", "KBS_ENCLAVE_NAME", "\"823a3b2c037ff0cbe305cc48928cfcc97c9ed4a8ca6d49af6f7d6981fb60a4e9\"" buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx\"" buildConfigField "String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"\"" } diff --git a/app/src/main/java/org/thoughtcrime/securesms/AppCapabilities.java b/app/src/main/java/org/thoughtcrime/securesms/AppCapabilities.java index 6f6e2116dc..ef8cb572a8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/AppCapabilities.java +++ b/app/src/main/java/org/thoughtcrime/securesms/AppCapabilities.java @@ -10,8 +10,11 @@ public final class AppCapabilities { private static final boolean UUID_CAPABLE = false; private static final boolean GROUPS_V2_CAPABLE = false; - public static SignalServiceProfile.Capabilities getCapabilities() { - return new SignalServiceProfile.Capabilities(UUID_CAPABLE, - GROUPS_V2_CAPABLE); + /** + * @param storageCapable Whether or not the user can use storage service. This is another way of + * asking if the user has set a Signal PIN or not. + */ + public static SignalServiceProfile.Capabilities getCapabilities(boolean storageCapable) { + return new SignalServiceProfile.Capabilities(UUID_CAPABLE, GROUPS_V2_CAPABLE, storageCapable); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java b/app/src/main/java/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java index 0f8cbf89d1..c2d8a2c986 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java @@ -16,7 +16,6 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.lock.v2.CreateKbsPinActivity; -import org.thoughtcrime.securesms.lock.v2.PinUtil; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.migrations.ApplicationMigrationActivity; import org.thoughtcrime.securesms.migrations.ApplicationMigrations; @@ -26,7 +25,6 @@ import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.registration.RegistrationNavigationActivity; import org.thoughtcrime.securesms.service.KeyCachingService; -import org.thoughtcrime.securesms.util.CensorshipUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import java.util.Locale; @@ -182,9 +180,7 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA } private boolean userMustSetKbsPin() { - // TODO [greyson] [pins] Maybe re-enable in the future -// return !SignalStore.registrationValues().isRegistrationComplete() && !PinUtil.userHasPin(this); - return false; + return !SignalStore.registrationValues().isRegistrationComplete() && !SignalStore.kbsValues().hasPin(); } private boolean userMustSetProfileName() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java index 491e620bb5..e214f20b98 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java @@ -102,7 +102,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.events.ReminderUpdateEvent; import org.thoughtcrime.securesms.insights.InsightsLauncher; import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob; -import org.thoughtcrime.securesms.lock.RegistrationLockDialog; +import org.thoughtcrime.securesms.lock.RegistrationLockV1Dialog; import org.thoughtcrime.securesms.lock.v2.CreateKbsPinActivity; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.mediasend.MediaSendActivity; @@ -238,7 +238,7 @@ public class ConversationListFragment extends MainFragment implements LoaderMana RatingManager.showRatingDialogIfNecessary(requireContext()); - RegistrationLockDialog.showReminderIfNecessary(this); + RegistrationLockV1Dialog.showReminderIfNecessary(this); TooltipCompat.setTooltipText(searchAction, getText(R.string.SearchToolbar_search_for_conversations_contacts_and_messages)); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencies.java b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencies.java index f27a056664..f76eb29970 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencies.java +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencies.java @@ -73,8 +73,8 @@ public class ApplicationDependencies { public static synchronized @NonNull KeyBackupService getKeyBackupService() { return getSignalServiceAccountManager().getKeyBackupService(IasKeyStore.getIasKeyStore(application), - BuildConfig.KEY_BACKUP_ENCLAVE_NAME, - BuildConfig.KEY_BACKUP_MRENCLAVE, + BuildConfig.KBS_ENCLAVE_NAME, + BuildConfig.KBS_MRENCLAVE, 10); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java index afbe60cae5..413a1202f2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -31,7 +31,7 @@ import org.thoughtcrime.securesms.migrations.RecipientSearchMigrationJob; import org.thoughtcrime.securesms.migrations.RegistrationPinV2MigrationJob; import org.thoughtcrime.securesms.migrations.StickerAdditionMigrationJob; import org.thoughtcrime.securesms.migrations.StickerLaunchMigrationJob; -import org.thoughtcrime.securesms.migrations.StorageKeyRotationMigrationJob; +import org.thoughtcrime.securesms.migrations.StorageCapabilityMigrationJob; import org.thoughtcrime.securesms.migrations.StorageServiceMigrationJob; import org.thoughtcrime.securesms.migrations.UuidMigrationJob; @@ -119,7 +119,7 @@ public final class JobManagerFactories { put(RegistrationPinV2MigrationJob.KEY, new RegistrationPinV2MigrationJob.Factory()); put(StickerLaunchMigrationJob.KEY, new StickerLaunchMigrationJob.Factory()); put(StickerAdditionMigrationJob.KEY, new StickerAdditionMigrationJob.Factory()); - put(StorageKeyRotationMigrationJob.KEY, new StorageKeyRotationMigrationJob.Factory()); + put(StorageCapabilityMigrationJob.KEY, new StorageCapabilityMigrationJob.Factory()); put(StorageServiceMigrationJob.KEY, new StorageServiceMigrationJob.Factory()); put(UuidMigrationJob.KEY, new UuidMigrationJob.Factory()); @@ -132,6 +132,7 @@ public final class JobManagerFactories { put("RefreshUnidentifiedDeliveryAbilityJob", new FailingJob.Factory()); put("Argon2TestJob", new FailingJob.Factory()); put("Argon2TestMigrationJob", new PassingMigrationJob.Factory()); + put("StorageKeyRotationMigrationJob", new PassingMigrationJob.Factory()); }}; } 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 0522d18919..691f10271c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceKeysUpdateJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceKeysUpdateJob.java @@ -60,7 +60,7 @@ public class MultiDeviceKeysUpdateJob extends BaseJob { } SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender(); - StorageKey storageServiceKey = SignalStore.storageServiceValues().getOrCreateStorageMasterKey().deriveStorageServiceKey(); + StorageKey storageServiceKey = SignalStore.storageServiceValues().getOrCreateStorageKey(); messageSender.sendMessage(SignalServiceSyncMessage.forKeys(new KeysMessage(Optional.fromNullable(storageServiceKey))), UnidentifiedAccessUtil.getAccessForSync(context)); 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 f4771b6fad..a1d70739f5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java @@ -1,5 +1,7 @@ package org.thoughtcrime.securesms.jobs; +import android.text.TextUtils; + import androidx.annotation.NonNull; import org.thoughtcrime.securesms.AppCapabilities; @@ -51,22 +53,24 @@ public class RefreshAttributesJob extends BaseJob { boolean fetchesMessages = TextSecurePreferences.isFcmDisabled(context); byte[] unidentifiedAccessKey = UnidentifiedAccess.deriveAccessKeyFrom(ProfileKeyUtil.getSelfProfileKey()); boolean universalUnidentifiedAccess = TextSecurePreferences.isUniversalUnidentifiedAccess(context); - String pin = null; - String registrationLockToken = null; + String registrationLockV1 = null; + String registrationLockV2 = null; KbsValues kbsValues = SignalStore.kbsValues(); if (kbsValues.isV2RegistrationLockEnabled()) { - registrationLockToken = kbsValues.getRegistrationLockToken(); + registrationLockV2 = kbsValues.getRegistrationLockToken(); } else if (TextSecurePreferences.isV1RegistrationLockEnabled(context)) { //noinspection deprecation Ok to read here as they have not migrated - pin = TextSecurePreferences.getDeprecatedV1RegistrationLockPin(context); + registrationLockV1 = TextSecurePreferences.getDeprecatedV1RegistrationLockPin(context); } + Log.i(TAG, "Calling setAccountAttributes() reglockV1? " + !TextUtils.isEmpty(registrationLockV1) + ", reglockV2? " + !TextUtils.isEmpty(registrationLockV2) + ", pin? " + kbsValues.hasPin()); + SignalServiceAccountManager signalAccountManager = ApplicationDependencies.getSignalServiceAccountManager(); signalAccountManager.setAccountAttributes(null, registrationId, fetchesMessages, - pin, registrationLockToken, + registrationLockV1, registrationLockV2, unidentifiedAccessKey, universalUnidentifiedAccess, - AppCapabilities.getCapabilities()); + AppCapabilities.getCapabilities(kbsValues.hasPin())); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageAccountRestoreJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageAccountRestoreJob.java index 6950f85d41..281f3b27cc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageAccountRestoreJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageAccountRestoreJob.java @@ -10,6 +10,7 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.storage.StorageSyncHelper; +import org.thoughtcrime.securesms.util.Base64; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; @@ -63,7 +64,7 @@ public class StorageAccountRestoreJob extends BaseJob { @Override protected void onRun() throws Exception { SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager(); - StorageKey storageServiceKey = SignalStore.storageServiceValues().getOrCreateStorageMasterKey().deriveStorageServiceKey(); + StorageKey storageServiceKey = SignalStore.storageServiceValues().getOrCreateStorageKey(); Optional manifest = accountManager.getStorageManifest(storageServiceKey); 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 19d4f40c0b..93df306804 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageForcePushJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageForcePushJob.java @@ -73,7 +73,7 @@ public class StorageForcePushJob extends BaseJob { @Override protected void onRun() throws IOException, RetryLaterException { - StorageKey storageServiceKey = SignalStore.storageServiceValues().getOrCreateStorageMasterKey().deriveStorageServiceKey(); + StorageKey storageServiceKey = SignalStore.storageServiceValues().getOrCreateStorageKey(); SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager(); RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context); StorageKeyDatabase storageKeyDatabase = DatabaseFactory.getStorageKeyDatabase(context); 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 219aa071f3..d788726cb0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.java @@ -26,6 +26,7 @@ import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.storage.StorageSyncValidations; import org.thoughtcrime.securesms.transport.RetryLaterException; +import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; @@ -86,11 +87,16 @@ public class StorageSyncJob extends BaseJob { @Override protected void onRun() throws IOException, RetryLaterException { - if (!FeatureFlags.storageService()) { + if (!FeatureFlags.pinsForAll()) { Log.i(TAG, "Not enabled. Skipping."); return; } + if (!SignalStore.kbsValues().hasPin()) { + Log.i(TAG, "Doesn't have a PIN. Skipping."); + return; + } + if (!TextSecurePreferences.isPushRegistered(context)) { Log.i(TAG, "Not registered. Skipping."); return; @@ -127,7 +133,7 @@ public class StorageSyncJob extends BaseJob { SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager(); RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context); StorageKeyDatabase storageKeyDatabase = DatabaseFactory.getStorageKeyDatabase(context); - StorageKey storageServiceKey = SignalStore.storageServiceValues().getOrCreateStorageMasterKey().deriveStorageServiceKey(); + StorageKey storageServiceKey = SignalStore.storageServiceValues().getOrCreateStorageKey(); boolean needsMultiDeviceSync = false; long localManifestVersion = TextSecurePreferences.getStorageManifestVersion(context); diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KbsValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KbsValues.java index 0cc7a09432..b50ebca69d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KbsValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KbsValues.java @@ -3,8 +3,10 @@ package org.thoughtcrime.securesms.keyvalue; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.JsonUtils; -import org.whispersystems.signalservice.api.RegistrationLockData; +import org.whispersystems.signalservice.api.KbsPinData; import org.whispersystems.signalservice.api.kbs.MasterKey; import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse; @@ -13,7 +15,7 @@ import java.security.SecureRandom; public final class KbsValues { - private static final String V2_LOCK_ENABLED = "kbs.v2_lock_enabled"; + public static final String V2_LOCK_ENABLED = "kbs.v2_lock_enabled"; private static final String MASTER_KEY = "kbs.registration_lock_master_key"; private static final String TOKEN_RESPONSE = "kbs.token_response"; private static final String LOCK_LOCAL_PIN_HASH = "kbs.registration_lock_local_pin_hash"; @@ -27,7 +29,7 @@ public final class KbsValues { /** * Deliberately does not clear the {@link #MASTER_KEY}. */ - public void clearRegistrationLock() { + public void clearRegistrationLockAndPin() { store.beginWrite() .remove(V2_LOCK_ENABLED) .remove(TOKEN_RESPONSE) @@ -35,23 +37,30 @@ public final class KbsValues { .commit(); } - public synchronized void setRegistrationLockMasterKey(@NonNull RegistrationLockData registrationLockData, @NonNull String localPinHash) { - MasterKey masterKey = registrationLockData.getMasterKey(); + public synchronized void setKbsMasterKey(@NonNull KbsPinData pinData, @NonNull String localPinHash) { + MasterKey masterKey = pinData.getMasterKey(); String tokenResponse; try { - tokenResponse = JsonUtils.toJson(registrationLockData.getTokenResponse()); + tokenResponse = JsonUtils.toJson(pinData.getTokenResponse()); } catch (IOException e) { throw new AssertionError(e); } store.beginWrite() - .putBoolean(V2_LOCK_ENABLED, true) .putString(TOKEN_RESPONSE, tokenResponse) .putBlob(MASTER_KEY, masterKey.serialize()) .putString(LOCK_LOCAL_PIN_HASH, localPinHash) .commit(); } + public synchronized void setV2RegistrationLockEnabled(boolean enabled) { + store.beginWrite().putBoolean(V2_LOCK_ENABLED, enabled).apply(); + } + + public synchronized boolean isV2RegistrationLockEnabled() { + return store.getBoolean(V2_LOCK_ENABLED, false); + } + /** * Finds or creates the master key. Therefore this will always return a master key whether backed * up or not. @@ -97,8 +106,8 @@ public final class KbsValues { return store.getString(LOCK_LOCAL_PIN_HASH, null); } - public synchronized boolean isV2RegistrationLockEnabled() { - return store.getBoolean(V2_LOCK_ENABLED, false); + public synchronized boolean hasPin() { + return getLocalPinHash() != null; } public synchronized @Nullable TokenResponse getRegistrationLockTokenResponse() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/PinValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/PinValues.java index adb3237dca..fbcce8af17 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/PinValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/PinValues.java @@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.keyvalue; import androidx.annotation.CheckResult; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.lock.SignalPinReminders; @@ -19,6 +20,7 @@ public final class PinValues { private static final String LAST_SUCCESSFUL_ENTRY = "pin.last_successful_entry"; private static final String NEXT_INTERVAL = "pin.interval_index"; private static final String KEYBOARD_TYPE = "kbs.keyboard_type"; + private static final String PIN_STATE = "pin.pin_state"; private final KeyValueStore store; @@ -55,9 +57,9 @@ public final class PinValues { .apply(); } - public void onPinChange() { + public void resetPinReminders() { long nextInterval = SignalPinReminders.INITIAL_INTERVAL; - Log.i(TAG, "onPinChange() nextInterval: " + nextInterval); + Log.i(TAG, "resetPinReminders() nextInterval: " + nextInterval, new Throwable()); store.beginWrite() .putLong(NEXT_INTERVAL, nextInterval) @@ -82,4 +84,12 @@ public final class PinValues { public @NonNull PinKeyboardType getKeyboardType() { return PinKeyboardType.fromCode(store.getString(KEYBOARD_TYPE, null)); } + + public void setPinState(@NonNull String pinState) { + store.beginWrite().putString(PIN_STATE, pinState).commit(); + } + + public @Nullable String getPinState() { + return store.getString(PIN_STATE, null); + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalPreferenceDataStore.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalPreferenceDataStore.java new file mode 100644 index 0000000000..a68366b089 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalPreferenceDataStore.java @@ -0,0 +1,70 @@ +package org.thoughtcrime.securesms.keyvalue; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.preference.PreferenceDataStore; + +import java.util.Set; + +/** + * An implementation of the {@link PreferenceDataStore} interface to let us link preference screens + * to the {@link SignalStore}. + */ +public class SignalPreferenceDataStore extends PreferenceDataStore { + + private final KeyValueStore store; + + SignalPreferenceDataStore(@NonNull KeyValueStore store) { + this.store = store; + } + + @Override + public void putString(String key, @Nullable String value) { + store.beginWrite().putString(key, value).apply(); + } + + @Override + public void putInt(String key, int value) { + store.beginWrite().putInteger(key, value).apply(); + } + + @Override + public void putLong(String key, long value) { + store.beginWrite().putLong(key, value).apply(); + } + + @Override + public void putFloat(String key, float value) { + store.beginWrite().putFloat(key, value).apply(); + } + + @Override + public void putBoolean(String key, boolean value) { + store.beginWrite().putBoolean(key, value).apply(); + } + + @Override + public @Nullable String getString(String key, @Nullable String defValue) { + return store.getString(key, defValue); + } + + @Override + public int getInt(String key, int defValue) { + return store.getInteger(key, defValue); + } + + @Override + public long getLong(String key, long defValue) { + return store.getLong(key, defValue); + } + + @Override + public float getFloat(String key, float defValue) { + return store.getFloat(key, defValue); + } + + @Override + public boolean getBoolean(String key, boolean defValue) { + return store.getBoolean(key, defValue); + } +} 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 1ec6382866..2115829231 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStore.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStore.java @@ -1,10 +1,10 @@ package org.thoughtcrime.securesms.keyvalue; import androidx.annotation.NonNull; +import androidx.preference.PreferenceDataStore; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.logging.SignalUncaughtExceptionHandler; -import org.thoughtcrime.securesms.util.FeatureFlags; /** * Simple, encrypted key-value store. @@ -56,6 +56,10 @@ public final class SignalStore { putLong(MESSAGE_REQUEST_ENABLE_TIME, time); } + public static @NonNull PreferenceDataStore getPreferenceDataStore() { + return new SignalPreferenceDataStore(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/keyvalue/StorageServiceValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/StorageServiceValues.java index 4349bed772..676ae73b53 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/StorageServiceValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/StorageServiceValues.java @@ -4,13 +4,13 @@ import androidx.annotation.NonNull; import org.thoughtcrime.securesms.util.FeatureFlags; import org.whispersystems.signalservice.api.kbs.MasterKey; +import org.whispersystems.signalservice.api.storage.StorageKey; import java.security.SecureRandom; public class StorageServiceValues { - private static final String STORAGE_MASTER_KEY = "storage.storage_master_key"; - private static final String LAST_SYNC_TIME = "storage.last_sync_time"; + private static final String LAST_SYNC_TIME = "storage.last_sync_time"; private final KeyValueStore store; @@ -18,23 +18,8 @@ public class StorageServiceValues { this.store = store; } - public synchronized MasterKey getOrCreateStorageMasterKey() { - byte[] blob = store.getBlob(STORAGE_MASTER_KEY, null); - - if (blob == null) { - store.beginWrite() - .putBlob(STORAGE_MASTER_KEY, MasterKey.createNew(new SecureRandom()).serialize()) - .commit(); - blob = store.getBlob(STORAGE_MASTER_KEY, null); - } - - return new MasterKey(blob); - } - - public synchronized void rotateStorageMasterKey() { - store.beginWrite() - .putBlob(STORAGE_MASTER_KEY, MasterKey.createNew(new SecureRandom()).serialize()) - .commit(); + public synchronized StorageKey getOrCreateStorageKey() { + return SignalStore.kbsValues().getOrCreateMasterKey().deriveStorageServiceKey(); } public long getLastSyncTime() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockDialog.java b/app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockV1Dialog.java similarity index 73% rename from app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockDialog.java rename to app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockV1Dialog.java index 0ab69f8c52..0a7a75d796 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockDialog.java +++ b/app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockV1Dialog.java @@ -9,7 +9,6 @@ import android.text.Editable; import android.text.SpannableString; import android.text.SpannableStringBuilder; import android.text.Spanned; -import android.text.TextUtils; import android.text.TextWatcher; import android.text.method.LinkMovementMethod; import android.text.style.ClickableSpan; @@ -33,35 +32,27 @@ import androidx.fragment.app.Fragment; 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.lock.v2.KbsConstants; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.migrations.RegistrationPinV2MigrationJob; +import org.thoughtcrime.securesms.pin.PinState; import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.ThemeUtil; import org.thoughtcrime.securesms.util.text.AfterTextChanged; -import org.whispersystems.signalservice.api.KeyBackupService; -import org.whispersystems.signalservice.api.KeyBackupServicePinException; -import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException; -import org.whispersystems.signalservice.api.RegistrationLockData; -import org.whispersystems.signalservice.api.kbs.HashedPin; -import org.whispersystems.signalservice.api.kbs.MasterKey; import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException; -import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse; import java.io.IOException; -public final class RegistrationLockDialog { +public final class RegistrationLockV1Dialog { - private static final String TAG = Log.tag(RegistrationLockDialog.class); + private static final String TAG = Log.tag(RegistrationLockV1Dialog.class); public static void showReminderIfNecessary(@NonNull Fragment fragment) { final Context context = fragment.requireContext(); - if (!TextSecurePreferences.isV1RegistrationLockEnabled(context) && !SignalStore.kbsValues().isV2RegistrationLockEnabled()) { + if (!PinState.shouldShowRegistrationLockV1Reminder()) { return; } @@ -124,9 +115,7 @@ public final class RegistrationLockDialog { reminder.setText(new SpannableStringBuilder(reminderIntro).append(" ").append(reminderText).append(" ").append(forgotText)); reminder.setMovementMethod(LinkMovementMethod.getInstance()); - pinEditText.addTextChangedListener(SignalStore.kbsValues().isV2RegistrationLockEnabled() - ? getV2PinWatcher(context, dialog) - : getV1PinWatcher(context, dialog)); + pinEditText.addTextChangedListener(getV1PinWatcher(context, dialog)); } private static TextWatcher getV1PinWatcher(@NonNull Context context, AlertDialog dialog) { @@ -144,25 +133,6 @@ public final class RegistrationLockDialog { }); } - private static TextWatcher getV2PinWatcher(@NonNull Context context, AlertDialog dialog) { - KbsValues kbsValues = SignalStore.kbsValues(); - String localPinHash = kbsValues.getLocalPinHash(); - - if (localPinHash == null) throw new AssertionError("No local pin hash set at time of reminder"); - - return new AfterTextChanged((Editable s) -> { - if (s == null) return; - String pin = s.toString(); - if (TextUtils.isEmpty(pin)) return; - if (pin.length() < KbsConstants.MINIMUM_PIN_LENGTH) return; - - if (PinHashing.verifyLocalPinHash(localPinHash, pin)) { - dialog.dismiss(); - RegistrationLockReminders.scheduleReminder(context, true); - } - }); - } - @SuppressLint("StaticFieldLeak") public static void showRegistrationLockPrompt(@NonNull Context context, @NonNull SwitchPreferenceCompat preference) { AlertDialog dialog = new AlertDialog.Builder(context) @@ -210,19 +180,7 @@ public final class RegistrationLockDialog { protected Boolean doInBackground(Void... voids) { try { Log.i(TAG, "Setting pin on KBS - dialog"); - - 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); - - kbsValues.setRegistrationLockMasterKey(kbsData, PinHashing.localPinHash(pinValue)); - TextSecurePreferences.clearOldRegistrationLockPin(context); - TextSecurePreferences.setRegistrationLockLastReminderTime(context, System.currentTimeMillis()); - TextSecurePreferences.setRegistrationLockNextReminderInterval(context, RegistrationLockReminders.INITIAL_INTERVAL); - + PinState.onCompleteRegistrationLockV1Reminder(context, pinValue); Log.i(TAG, "Pin set on KBS"); return true; } catch (IOException | UnauthenticatedResponseException e) { @@ -277,23 +235,7 @@ public final class RegistrationLockDialog { @Override protected Boolean doInBackground(Void... voids) { try { - KbsValues kbsValues = SignalStore.kbsValues(); - - 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)) { - Log.i(TAG, "Removing v1 registration lock pin from server"); - ApplicationDependencies.getSignalServiceAccountManager().removeV1Pin(); - } - TextSecurePreferences.clearOldRegistrationLockPin(context); + PinState.onDisableRegistrationLockV1(context); return true; } catch (IOException | UnauthenticatedResponseException e) { Log.w(TAG, e); diff --git a/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinFragment.java b/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinFragment.java index bdacc0e5e4..5d14f84a9d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinFragment.java @@ -2,16 +2,13 @@ package org.thoughtcrime.securesms.lock.v2; import android.animation.Animator; import android.app.Activity; -import android.content.Context; import android.content.Intent; import android.view.View; -import android.view.inputmethod.InputMethodManager; import androidx.annotation.NonNull; import androidx.annotation.RawRes; import androidx.appcompat.app.AlertDialog; import androidx.core.content.ContextCompat; -import androidx.core.util.Preconditions; import androidx.lifecycle.ViewModelProviders; import com.airbnb.lottie.LottieAnimationView; @@ -25,6 +22,8 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.megaphone.Megaphones; import org.thoughtcrime.securesms.util.SpanUtil; +import java.util.Objects; + public class ConfirmKbsPinFragment extends BaseKbsPinFragment { private ConfirmKbsPinViewModel viewModel; @@ -43,7 +42,7 @@ public class ConfirmKbsPinFragment extends BaseKbsPinFragment { try { 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); - - kbsValues.setRegistrationLockMasterKey(kbsData, PinHashing.localPinHash(pinValue)); - TextSecurePreferences.clearOldRegistrationLockPin(context); - TextSecurePreferences.setRegistrationLockLastReminderTime(context, System.currentTimeMillis()); - TextSecurePreferences.setRegistrationLockNextReminderInterval(context, RegistrationLockReminders.INITIAL_INTERVAL); - SignalStore.pinValues().setKeyboardType(keyboard); - ApplicationDependencies.getMegaphoneRepository().markFinished(Megaphones.Event.PINS_FOR_ALL); - + PinState.onPinChangedOrCreated(context, pinValue, keyboard); Log.i(TAG, "Pin set on KBS"); + return PinSetResult.SUCCESS; } catch (IOException | UnauthenticatedResponseException e) { Log.w(TAG, e); diff --git a/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinViewModel.java index 8a77c85f1f..ea0a9d2a79 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinViewModel.java @@ -2,23 +2,22 @@ package org.thoughtcrime.securesms.lock.v2; import androidx.annotation.MainThread; import androidx.annotation.NonNull; -import androidx.core.util.Preconditions; import androidx.lifecycle.LiveData; -import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.Transformations; import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModelProvider; import org.thoughtcrime.securesms.lock.v2.ConfirmKbsPinRepository.PinSetResult; +import org.thoughtcrime.securesms.util.DefaultValueLiveData; final class ConfirmKbsPinViewModel extends ViewModel implements BaseKbsPinViewModel { private final ConfirmKbsPinRepository repository; - private final MutableLiveData userEntry = new MutableLiveData<>(KbsPin.EMPTY); - private final MutableLiveData keyboard = new MutableLiveData<>(PinKeyboardType.NUMERIC); - private final MutableLiveData saveAnimation = new MutableLiveData<>(SaveAnimation.NONE); - private final MutableLiveData