diff --git a/app/src/main/java/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java b/app/src/main/java/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java index c902c0e19a..91f4894acd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java @@ -20,7 +20,6 @@ import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.migrations.ApplicationMigrationActivity; import org.thoughtcrime.securesms.migrations.ApplicationMigrations; import org.thoughtcrime.securesms.pin.PinRestoreActivity; -import org.thoughtcrime.securesms.profiles.ProfileName; import org.thoughtcrime.securesms.profiles.edit.EditProfileActivity; import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess; import org.thoughtcrime.securesms.recipients.Recipient; @@ -186,7 +185,7 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA } private boolean userMustCreateSignalPin() { - return !SignalStore.registrationValues().isRegistrationComplete() && !SignalStore.kbsValues().hasPin(); + return !SignalStore.registrationValues().isRegistrationComplete() && !SignalStore.kbsValues().hasPin() && !SignalStore.kbsValues().lastPinCreateFailed(); } private boolean userMustSetProfileName() { 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 d2ab64668d..a6a13ba10c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KbsValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KbsValues.java @@ -15,10 +15,11 @@ import java.security.SecureRandom; public final class KbsValues { - 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"; + 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"; + private static final String LAST_CREATE_FAILED_TIMESTAMP = "kbs.last_create_failed_timestamp"; private final KeyValueStore store; @@ -36,6 +37,7 @@ public final class KbsValues { .remove(V2_LOCK_ENABLED) .remove(TOKEN_RESPONSE) .remove(LOCK_LOCAL_PIN_HASH) + .remove(LAST_CREATE_FAILED_TIMESTAMP) .commit(); } @@ -53,6 +55,7 @@ public final class KbsValues { .putString(TOKEN_RESPONSE, tokenResponse) .putBlob(MASTER_KEY, masterKey.serialize()) .putString(LOCK_LOCAL_PIN_HASH, localPinHash) + .putLong(LAST_CREATE_FAILED_TIMESTAMP, -1) .commit(); } @@ -61,10 +64,25 @@ public final class KbsValues { store.beginWrite().putBoolean(V2_LOCK_ENABLED, enabled).apply(); } + /** + * Whether or not registration lock V2 is enabled. + */ public synchronized boolean isV2RegistrationLockEnabled() { return store.getBoolean(V2_LOCK_ENABLED, false); } + /** Should only be set by {@link org.thoughtcrime.securesms.pin.PinState}. */ + public synchronized void onPinCreateFailure() { + store.beginWrite().putLong(LAST_CREATE_FAILED_TIMESTAMP, System.currentTimeMillis()).apply(); + } + + /** + * Whether or not the last time the user attempted to create a PIN, it failed. + */ + public synchronized boolean lastPinCreateFailed() { + return store.getLong(LAST_CREATE_FAILED_TIMESTAMP, -1) > 0; + } + /** * Finds or creates the master key. Therefore this will always return a master key whether backed * up or not. diff --git a/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinRepository.java b/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinRepository.java index e6023d6a59..89def2a3c4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinRepository.java @@ -31,6 +31,7 @@ final class ConfirmKbsPinRepository { return PinSetResult.SUCCESS; } catch (IOException | UnauthenticatedResponseException e) { Log.w(TAG, e); + PinState.onPinCreateFailure(); return PinSetResult.FAILURE; } }, resultConsumer::accept); diff --git a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionPin.java b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionPin.java index b4f46b3c0d..dc00b2bfa6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionPin.java +++ b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionPin.java @@ -22,6 +22,7 @@ public class LogSectionPin implements LogSection { .append("ReglockV1: ").append(TextSecurePreferences.isV1RegistrationLockEnabled(context)).append("\n") .append("ReglockV2: ").append(SignalStore.kbsValues().isV2RegistrationLockEnabled()).append("\n") .append("Signal PIN: ").append(SignalStore.kbsValues().hasPin()).append("\n") + .append("Last Creation Failed: ").append(SignalStore.kbsValues().lastPinCreateFailed()).append("\n") .append("Needs Account Restore: ").append(SignalStore.storageServiceValues().needsAccountRestore()).append("\n") .append("PIN Required at Registration: ").append(SignalStore.registrationValues().pinWasRequiredAtRegistration()).append("\n") .append("Registration Complete: ").append(SignalStore.registrationValues().isRegistrationComplete()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/megaphone/PinsForAllSchedule.java b/app/src/main/java/org/thoughtcrime/securesms/megaphone/PinsForAllSchedule.java index b133df9df5..180c7c09a4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/megaphone/PinsForAllSchedule.java +++ b/app/src/main/java/org/thoughtcrime/securesms/megaphone/PinsForAllSchedule.java @@ -4,17 +4,21 @@ import androidx.annotation.VisibleForTesting; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.keyvalue.SignalStore; +import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.TextSecurePreferences; +import java.util.Locale; import java.util.concurrent.TimeUnit; class PinsForAllSchedule implements MegaphoneSchedule { + private static final String TAG = Log.tag(PinsForAllSchedule.class); + @VisibleForTesting static final long DAYS_UNTIL_FULLSCREEN = 8L; - private final MegaphoneSchedule schedule = new RecurringSchedule(TimeUnit.DAYS.toMillis(2)); + private final MegaphoneSchedule schedule = new RecurringSchedule(TimeUnit.HOURS.toMillis(2)); static boolean shouldDisplayFullScreen(long firstVisible, long currentTime) { if (!FeatureFlags.pinsForAllMandatory()) { @@ -37,7 +41,9 @@ class PinsForAllSchedule implements MegaphoneSchedule { if (shouldDisplayFullScreen(firstVisible, currentTime)) { return true; } else { - return schedule.shouldDisplay(seenCount, lastSeen, firstVisible, currentTime); + boolean shouldDisplay = schedule.shouldDisplay(seenCount, lastSeen, firstVisible, currentTime); + Log.i(TAG, String.format(Locale.ENGLISH, "seenCount: %d, lastSeen: %d, firstVisible: %d, currentTime: %d, result: %b", seenCount, lastSeen, firstVisible, currentTime, shouldDisplay)); + return shouldDisplay; } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/pin/PinState.java b/app/src/main/java/org/thoughtcrime/securesms/pin/PinState.java index 20d547a4b0..44d0b23a69 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/pin/PinState.java +++ b/app/src/main/java/org/thoughtcrime/securesms/pin/PinState.java @@ -27,6 +27,7 @@ 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 org.whispersystems.signalservice.internal.storage.protos.SignalStorage; import java.io.IOException; import java.util.Arrays; @@ -175,6 +176,15 @@ public final class PinState { updateState(buildInferredStateFromOtherFields()); } + /** + * Invoked when PIN creation fails. + */ + public static synchronized void onPinCreateFailure() { + if (getState() == State.NO_REGISTRATION_LOCK) { + SignalStore.kbsValues().onPinCreateFailure(); + } + } + /** * Invoked whenever a Signal PIN user enables registration lock. */ diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/service/RegistrationCodeRequest.java b/app/src/main/java/org/thoughtcrime/securesms/registration/service/RegistrationCodeRequest.java index 1d9ab8bf9d..85743754dd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/service/RegistrationCodeRequest.java +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/service/RegistrationCodeRequest.java @@ -33,7 +33,7 @@ public final class RegistrationCodeRequest { */ @SuppressLint("StaticFieldLeak") static void requestSmsVerificationCode(@NonNull Context context, @NonNull Credentials credentials, @Nullable String captchaToken, @NonNull Mode mode, @NonNull SmsVerificationCodeCallback callback) { - Log.d(TAG, String.format("SMS Verification requested for %s captcha %s", credentials.getE164number(), captchaToken)); + Log.d(TAG, "SMS Verification requested"); new AsyncTask() { @Override diff --git a/app/src/test/java/org/thoughtcrime/securesms/megaphone/PinsForAllScheduleTest.java b/app/src/test/java/org/thoughtcrime/securesms/megaphone/PinsForAllScheduleTest.java index 1103abb03d..196dabbbc7 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/megaphone/PinsForAllScheduleTest.java +++ b/app/src/test/java/org/thoughtcrime/securesms/megaphone/PinsForAllScheduleTest.java @@ -8,10 +8,12 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; +import org.thoughtcrime.securesms.BaseUnitTest; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.keyvalue.KbsValues; import org.thoughtcrime.securesms.keyvalue.RegistrationValues; import org.thoughtcrime.securesms.keyvalue.SignalStore; +import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.TextSecurePreferences; @@ -26,19 +28,22 @@ import static org.powermock.api.mockito.PowerMockito.mockStatic; import static org.powermock.api.mockito.PowerMockito.when; @RunWith(PowerMockRunner.class) -@PrepareForTest({ApplicationDependencies.class, SignalStore.class, FeatureFlags.class, RegistrationValues.class, KbsValues.class, TextSecurePreferences.class}) -public class PinsForAllScheduleTest { +@PrepareForTest({ApplicationDependencies.class, SignalStore.class, FeatureFlags.class, RegistrationValues.class, KbsValues.class, TextSecurePreferences.class }) +public class PinsForAllScheduleTest extends BaseUnitTest { private final PinsForAllSchedule testSubject = new PinsForAllSchedule(); private final RegistrationValues registrationValues = mock(RegistrationValues.class); private final KbsValues kbsValues = mock(KbsValues.class); @Before - public void setUp() { + public void setUp() throws Exception { + super.setUp(); + mockStatic(ApplicationDependencies.class); mockStatic(SignalStore.class); mockStatic(FeatureFlags.class); mockStatic(TextSecurePreferences.class); + mockStatic(Log.class); when(ApplicationDependencies.getApplication()).thenReturn(mock(Application.class)); when(SignalStore.registrationValues()).thenReturn(registrationValues); when(SignalStore.kbsValues()).thenReturn(kbsValues); diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/KeyBackupService.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/KeyBackupService.java index d209f32505..856129f19e 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/KeyBackupService.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/KeyBackupService.java @@ -180,8 +180,6 @@ public final class KeyBackupService { // if the number of tries has not fallen, the pin is correct we're just using an out of date token boolean canRetry = remainingTries == status.getTries(); Log.i(TAG, String.format(Locale.US, "Token MISMATCH %d %d", remainingTries, status.getTries())); - Log.i(TAG, String.format("Last token %s", Hex.toStringCondensed(token.getToken()))); - Log.i(TAG, String.format("Next token %s", Hex.toStringCondensed(nextToken.getToken()))); throw new TokenException(nextToken, canRetry); case MISSING: Log.i(TAG, "Restore OK! No data though");