mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-23 02:10:44 +01:00
Separate PINs from Registration Lock.
You can now have a PIN without having registration lock. Note: We still need to change the registration flow to allow non-reglock users to enter their PIN.
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
package org.thoughtcrime.securesms.registration.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -15,10 +14,7 @@ import androidx.navigation.ActivityNavigator;
|
||||
import org.thoughtcrime.securesms.MainActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.lock.v2.CreateKbsPinActivity;
|
||||
import org.thoughtcrime.securesms.lock.v2.PinUtil;
|
||||
import org.thoughtcrime.securesms.profiles.edit.EditProfileActivity;
|
||||
import org.thoughtcrime.securesms.util.CensorshipUtil;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
|
||||
public final class RegistrationCompleteFragment extends BaseRegistrationFragment {
|
||||
|
||||
|
||||
@@ -305,24 +305,19 @@ public final class RegistrationLockFragment extends BaseRegistrationFragment {
|
||||
private void handleSuccessfulPinEntry() {
|
||||
SignalStore.pinValues().setKeyboardType(getPinEntryKeyboardType());
|
||||
|
||||
if (FeatureFlags.storageServiceRestore()) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
SimpleTask.run(() -> {
|
||||
return ApplicationDependencies.getJobManager().runSynchronously(new StorageAccountRestoreJob(), StorageAccountRestoreJob.LIFESPAN);
|
||||
}, result -> {
|
||||
long elapsedTime = System.currentTimeMillis() - startTime;
|
||||
long startTime = System.currentTimeMillis();
|
||||
SimpleTask.run(() -> {
|
||||
return ApplicationDependencies.getJobManager().runSynchronously(new StorageAccountRestoreJob(), StorageAccountRestoreJob.LIFESPAN);
|
||||
}, result -> {
|
||||
long elapsedTime = System.currentTimeMillis() - startTime;
|
||||
|
||||
if (result.isPresent()) {
|
||||
Log.i(TAG, "Storage Service account restore completed: " + result.get().name() + ". (Took " + elapsedTime + " ms)");
|
||||
} else {
|
||||
Log.i(TAG, "Storage Service account restore failed to complete in the allotted time. (" + elapsedTime + " ms elapsed)");
|
||||
}
|
||||
cancelSpinning(pinButton);
|
||||
Navigation.findNavController(requireView()).navigate(RegistrationLockFragmentDirections.actionSuccessfulRegistration());
|
||||
});
|
||||
} else {
|
||||
if (result.isPresent()) {
|
||||
Log.i(TAG, "Storage Service account restore completed: " + result.get().name() + ". (Took " + elapsedTime + " ms)");
|
||||
} else {
|
||||
Log.i(TAG, "Storage Service account restore failed to complete in the allotted time. (" + elapsedTime + " ms elapsed)");
|
||||
}
|
||||
cancelSpinning(pinButton);
|
||||
Navigation.findNavController(requireView()).navigate(RegistrationLockFragmentDirections.actionSuccessfulRegistration());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.registration.service;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -19,11 +20,8 @@ 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.KbsValues;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.lock.PinHashing;
|
||||
import org.thoughtcrime.securesms.lock.RegistrationLockReminders;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.pin.PinState;
|
||||
import org.thoughtcrime.securesms.push.AccountManagerFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
@@ -36,16 +34,11 @@ import org.whispersystems.libsignal.state.PreKeyRecord;
|
||||
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
||||
import org.whispersystems.libsignal.util.KeyHelper;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
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.KbsPinData;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
|
||||
import org.whispersystems.signalservice.api.kbs.HashedPin;
|
||||
import org.whispersystems.signalservice.api.kbs.MasterKey;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.RateLimitException;
|
||||
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException;
|
||||
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
|
||||
import org.whispersystems.signalservice.internal.push.LockedException;
|
||||
|
||||
@@ -194,7 +187,7 @@ public final class CodeVerificationRequest {
|
||||
@Nullable String fcmToken)
|
||||
throws IOException, KeyBackupSystemWrongPinException, KeyBackupSystemNoDataException
|
||||
{
|
||||
boolean isV2KbsPin = kbsTokenResponse != null;
|
||||
boolean isV2RegistrationLock = kbsTokenResponse != null;
|
||||
int registrationId = KeyHelper.generateRegistrationId(false);
|
||||
boolean universalUnidentifiedAccess = TextSecurePreferences.isUniversalUnidentifiedAccess(context);
|
||||
ProfileKey profileKey = findExistingProfileKey(context, credentials.getE164number());
|
||||
@@ -209,16 +202,24 @@ public final class CodeVerificationRequest {
|
||||
TextSecurePreferences.setLocalRegistrationId(context, registrationId);
|
||||
SessionUtil.archiveAllSessions(context);
|
||||
|
||||
SignalServiceAccountManager accountManager = AccountManagerFactory.createUnauthenticated(context, credentials.getE164number(), credentials.getPassword());
|
||||
RegistrationLockData kbsData = isV2KbsPin ? restoreMasterKey(pin, kbsStorageCredentials, kbsTokenResponse) : null;
|
||||
String registrationLock = kbsData != null ? kbsData.getMasterKey().deriveRegistrationLock() : null;
|
||||
boolean present = fcmToken != null;
|
||||
String pinForServer = isV2KbsPin ? null : pin;
|
||||
SignalServiceAccountManager accountManager = AccountManagerFactory.createUnauthenticated(context, credentials.getE164number(), credentials.getPassword());
|
||||
KbsPinData kbsData = isV2RegistrationLock ? PinState.restoreMasterKey(pin, kbsStorageCredentials, kbsTokenResponse) : null;
|
||||
String registrationLockV2 = kbsData != null ? kbsData.getMasterKey().deriveRegistrationLock() : null;
|
||||
String registrationLockV1 = isV2RegistrationLock ? null : pin;
|
||||
boolean hasFcm = fcmToken != null;
|
||||
|
||||
UUID uuid = accountManager.verifyAccountWithCode(code, null, registrationId, !present,
|
||||
pinForServer, registrationLock,
|
||||
unidentifiedAccessKey, universalUnidentifiedAccess,
|
||||
AppCapabilities.getCapabilities());
|
||||
Log.i(TAG, "Calling verifyAccountWithCode(): reglockV1? " + !TextUtils.isEmpty(registrationLockV1) + ", reglockV2? " + !TextUtils.isEmpty(registrationLockV2));
|
||||
|
||||
UUID uuid = accountManager.verifyAccountWithCode(code,
|
||||
null,
|
||||
registrationId,
|
||||
!hasFcm,
|
||||
registrationLockV1,
|
||||
registrationLockV2,
|
||||
unidentifiedAccessKey,
|
||||
universalUnidentifiedAccess,
|
||||
AppCapabilities.getCapabilities(isV2RegistrationLock));
|
||||
// TODO [greyson] [pins] ^^ This needs to be updated. It's not just for reglock, but also if they needed to enter a PIN at all
|
||||
|
||||
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context);
|
||||
List<PreKeyRecord> records = PreKeyUtil.generatePreKeys(context);
|
||||
@@ -227,7 +228,7 @@ public final class CodeVerificationRequest {
|
||||
accountManager = AccountManagerFactory.createAuthenticated(context, uuid, credentials.getE164number(), credentials.getPassword());
|
||||
accountManager.setPreKeys(identityKey.getPublicKey(), signedPreKey, records);
|
||||
|
||||
if (present) {
|
||||
if (hasFcm) {
|
||||
accountManager.setGcmId(Optional.fromNullable(fcmToken));
|
||||
}
|
||||
|
||||
@@ -243,7 +244,7 @@ public final class CodeVerificationRequest {
|
||||
ApplicationDependencies.getRecipientCache().clearSelf();
|
||||
|
||||
TextSecurePreferences.setFcmToken(context, fcmToken);
|
||||
TextSecurePreferences.setFcmDisabled(context, !present);
|
||||
TextSecurePreferences.setFcmDisabled(context, !hasFcm);
|
||||
TextSecurePreferences.setWebsocketRegistered(context, true);
|
||||
|
||||
DatabaseFactory.getIdentityDatabase(context)
|
||||
@@ -257,20 +258,8 @@ public final class CodeVerificationRequest {
|
||||
TextSecurePreferences.setSignedPreKeyRegistered(context, true);
|
||||
TextSecurePreferences.setPromptedPushRegistration(context, true);
|
||||
TextSecurePreferences.setUnauthorizedReceived(context, false);
|
||||
if (kbsData == null) {
|
||||
SignalStore.kbsValues().clearRegistrationLock();
|
||||
//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);
|
||||
} else {
|
||||
SignalStore.kbsValues().setRegistrationLockMasterKey(kbsData, PinHashing.localPinHash(pin));
|
||||
repostPinToResetTries(context, pin, kbsData);
|
||||
}
|
||||
if (pin != null) {
|
||||
TextSecurePreferences.setRegistrationLockLastReminderTime(context, System.currentTimeMillis());
|
||||
TextSecurePreferences.setRegistrationLockNextReminderInterval(context, RegistrationLockReminders.INITIAL_INTERVAL);
|
||||
}
|
||||
|
||||
PinState.onRegistration(context, kbsData, pin);
|
||||
}
|
||||
|
||||
private static @Nullable ProfileKey findExistingProfileKey(@NonNull Context context, @NonNull String e164number) {
|
||||
@@ -284,62 +273,6 @@ public final class CodeVerificationRequest {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void repostPinToResetTries(@NonNull Context context, @Nullable String pin, @NonNull RegistrationLockData kbsData) {
|
||||
if (pin == null) return;
|
||||
|
||||
KeyBackupService keyBackupService = ApplicationDependencies.getKeyBackupService();
|
||||
|
||||
try {
|
||||
KbsValues kbsValues = SignalStore.kbsValues();
|
||||
MasterKey masterKey = kbsValues.getOrCreateMasterKey();
|
||||
KeyBackupService.PinChangeSession pinChangeSession = keyBackupService.newPinChangeSession(kbsData.getTokenResponse());
|
||||
HashedPin hashedPin = PinHashing.hashPin(pin, pinChangeSession);
|
||||
RegistrationLockData newData = pinChangeSession.setPin(hashedPin, masterKey);
|
||||
|
||||
kbsValues.setRegistrationLockMasterKey(newData, PinHashing.localPinHash(pin));
|
||||
TextSecurePreferences.clearOldRegistrationLockPin(context);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "May have failed to reset pin attempts!", e);
|
||||
} catch (UnauthenticatedResponseException e) {
|
||||
Log.w(TAG, "Failed to reset pin attempts", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable RegistrationLockData restoreMasterKey(@Nullable String pin,
|
||||
@Nullable String basicStorageCredentials,
|
||||
@NonNull TokenResponse tokenResponse)
|
||||
throws IOException, KeyBackupSystemWrongPinException, KeyBackupSystemNoDataException
|
||||
{
|
||||
if (pin == null) return null;
|
||||
|
||||
if (basicStorageCredentials == null) {
|
||||
throw new AssertionError("Cannot restore KBS key, no storage credentials supplied");
|
||||
}
|
||||
|
||||
KeyBackupService keyBackupService = ApplicationDependencies.getKeyBackupService();
|
||||
|
||||
Log.i(TAG, "Opening key backup service session");
|
||||
KeyBackupService.RestoreSession session = keyBackupService.newRegistrationSession(basicStorageCredentials, tokenResponse);
|
||||
|
||||
try {
|
||||
Log.i(TAG, "Restoring pin from KBS");
|
||||
HashedPin hashedPin = PinHashing.hashPin(pin, session);
|
||||
RegistrationLockData kbsData = session.restorePin(hashedPin);
|
||||
if (kbsData != null) {
|
||||
Log.i(TAG, "Found registration lock token on KBS.");
|
||||
} else {
|
||||
throw new AssertionError("Null not expected");
|
||||
}
|
||||
return kbsData;
|
||||
} catch (UnauthenticatedResponseException e) {
|
||||
Log.w(TAG, "Failed to restore key", e);
|
||||
throw new IOException(e);
|
||||
} catch (KeyBackupServicePinException e) {
|
||||
Log.w(TAG, "Incorrect pin", e);
|
||||
throw new KeyBackupSystemWrongPinException(e.getToken());
|
||||
}
|
||||
}
|
||||
|
||||
public interface VerifyCallback {
|
||||
|
||||
void onSuccessfulRegistration();
|
||||
|
||||
@@ -4,11 +4,11 @@ import androidx.annotation.NonNull;
|
||||
|
||||
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
|
||||
|
||||
final class KeyBackupSystemWrongPinException extends Exception {
|
||||
public final class KeyBackupSystemWrongPinException extends Exception {
|
||||
|
||||
private final TokenResponse tokenResponse;
|
||||
|
||||
KeyBackupSystemWrongPinException(@NonNull TokenResponse tokenResponse){
|
||||
public KeyBackupSystemWrongPinException(@NonNull TokenResponse tokenResponse){
|
||||
this.tokenResponse = tokenResponse;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user